source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/Get.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 15.9 KB
Line 
1/*
2 * Copyright 2000-2005 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.taskdefs;
19
20import org.apache.tools.ant.BuildException;
21import org.apache.tools.ant.Project;
22import org.apache.tools.ant.Task;
23import org.apache.tools.ant.util.FileUtils;
24import org.apache.tools.ant.util.JavaEnvUtils;
25
26import java.io.File;
27import java.io.FileOutputStream;
28import java.io.IOException;
29import java.io.InputStream;
30import java.io.PrintStream;
31import java.net.HttpURLConnection;
32import java.net.URL;
33import java.net.URLConnection;
34import java.util.Date;
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 */
46public class Get extends Task {
47
48 private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
49
50 private URL source; // required
51 private File dest; // required
52 private boolean verbose = false;
53 private boolean useTimestamp = false; //off by default
54 private boolean ignoreErrors = false;
55 private String uname = null;
56 private String 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 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 {
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 t = new Date(timestamp);
132 log("local file date : " + t.toString(), logLevel);
133 }
134 hasTimestamp = true;
135 }
136
137 //set up the URL connection
138 URLConnection 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 up = uname + ":" + pword;
147 String 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) {
161 HttpURLConnection httpConnection
162 = (HttpURLConnection) 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 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 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 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 fos = new FileOutputStream(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 t = new Date(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 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 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 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 p) {
336 this.pword = p;
337 }
338
339 /*********************************************************************
340 * BASE 64 encoding of a String or an array of bytes.
341 *
342 * Based on RFC 1421.
343 *
344 *********************************************************************/
345
346 protected static class Base64Converter {
347
348 public final char[] alphabet = {
349 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7
350 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15
351 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23
352 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31
353 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39
354 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47
355 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55
356 '4', '5', '6', '7', '8', '9', '+', '/'}; // 56 to 63
357
358 public String encode(String s) {
359 return encode(s.getBytes());
360 }
361
362 public String encode(byte[] octetString) {
363 int bits24;
364 int bits6;
365
366 char[] out = new char[((octetString.length - 1) / 3 + 1) * 4];
367 int outIndex = 0;
368 int i = 0;
369
370 while ((i + 3) <= octetString.length) {
371 // store the octets
372 bits24 = (octetString[i++] & 0xFF) << 16;
373 bits24 |= (octetString[i++] & 0xFF) << 8;
374 bits24 |= octetString[i++];
375
376 bits6 = (bits24 & 0x00FC0000) >> 18;
377 out[outIndex++] = alphabet[bits6];
378 bits6 = (bits24 & 0x0003F000) >> 12;
379 out[outIndex++] = alphabet[bits6];
380 bits6 = (bits24 & 0x00000FC0) >> 6;
381 out[outIndex++] = alphabet[bits6];
382 bits6 = (bits24 & 0x0000003F);
383 out[outIndex++] = alphabet[bits6];
384 }
385 if (octetString.length - i == 2) {
386 // store the octets
387 bits24 = (octetString[i] & 0xFF) << 16;
388 bits24 |= (octetString[i + 1] & 0xFF) << 8;
389 bits6 = (bits24 & 0x00FC0000) >> 18;
390 out[outIndex++] = alphabet[bits6];
391 bits6 = (bits24 & 0x0003F000) >> 12;
392 out[outIndex++] = alphabet[bits6];
393 bits6 = (bits24 & 0x00000FC0) >> 6;
394 out[outIndex++] = alphabet[bits6];
395
396 // padding
397 out[outIndex++] = '=';
398 } else if (octetString.length - i == 1) {
399 // store the octets
400 bits24 = (octetString[i] & 0xFF) << 16;
401 bits6 = (bits24 & 0x00FC0000) >> 18;
402 out[outIndex++] = alphabet[bits6];
403 bits6 = (bits24 & 0x0003F000) >> 12;
404 out[outIndex++] = alphabet[bits6];
405
406 // padding
407 out[outIndex++] = '=';
408 out[outIndex++] = '=';
409 }
410 return new String(out);
411 }
412 }
413
414 public interface DownloadProgress {
415 /**
416 * begin a download
417 */
418 public void beginDownload();
419
420 /**
421 * tick handler
422 *
423 */
424 public void onTick();
425
426 /**
427 * end a download
428 */
429 public void endDownload();
430 }
431
432 /**
433 * do nothing with progress info
434 */
435 public static class NullProgress implements DownloadProgress {
436
437 /**
438 * begin a download
439 */
440 public void beginDownload() {
441
442 }
443
444 /**
445 * tick handler
446 *
447 */
448 public void onTick() {
449 }
450
451 /**
452 * end a download
453 */
454 public void endDownload() {
455
456 }
457 }
458
459 /**
460 * verbose progress system prints to some output stream
461 */
462 public static class VerboseProgress implements DownloadProgress {
463 private int dots = 0;
464 PrintStream out;
465
466 public VerboseProgress(PrintStream out) {
467 this.out = out;
468 }
469
470 /**
471 * begin a download
472 */
473 public void beginDownload() {
474 dots = 0;
475 }
476
477 /**
478 * tick handler
479 *
480 */
481 public void onTick() {
482 out.print(".");
483 if (dots++ > 50) {
484 out.flush();
485 dots = 0;
486 }
487 }
488
489 /**
490 * end a download
491 */
492 public void endDownload() {
493 out.println();
494 out.flush();
495 }
496 }
497
498}
Note: See TracBrowser for help on using the repository browser.