You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Get.java 27 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  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. package org.apache.tools.ant.taskdefs;
  19. import java.io.File;
  20. import java.io.FileNotFoundException;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.io.OutputStream;
  25. import java.io.PrintStream;
  26. import java.net.HttpURLConnection;
  27. import java.net.URL;
  28. import java.net.URLConnection;
  29. import java.util.Date;
  30. import org.apache.tools.ant.BuildException;
  31. import org.apache.tools.ant.Project;
  32. import org.apache.tools.ant.Task;
  33. import org.apache.tools.ant.types.Mapper;
  34. import org.apache.tools.ant.types.Resource;
  35. import org.apache.tools.ant.types.ResourceCollection;
  36. import org.apache.tools.ant.types.resources.Resources;
  37. import org.apache.tools.ant.types.resources.URLProvider;
  38. import org.apache.tools.ant.types.resources.URLResource;
  39. import org.apache.tools.ant.util.FileNameMapper;
  40. import org.apache.tools.ant.util.FileUtils;
  41. /**
  42. * Gets a particular file from a URL source.
  43. * Options include verbose reporting, timestamp based fetches and controlling
  44. * actions on failures. NB: access through a firewall only works if the whole
  45. * Java runtime is correctly configured.
  46. *
  47. * @since Ant 1.1
  48. *
  49. * @ant.task category="network"
  50. */
  51. public class Get extends Task {
  52. private static final int NUMBER_RETRIES = 3;
  53. private static final int DOTS_PER_LINE = 50;
  54. private static final int BIG_BUFFER_SIZE = 100 * 1024;
  55. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  56. private static final int REDIRECT_LIMIT = 25;
  57. private static final String HTTP = "http";
  58. private static final String HTTPS = "https";
  59. private Resources sources = new Resources();
  60. private File destination; // required
  61. private boolean verbose = false;
  62. private boolean useTimestamp = false; //off by default
  63. private boolean ignoreErrors = false;
  64. private String uname = null;
  65. private String pword = null;
  66. private long maxTime = 0;
  67. private int numberRetries = NUMBER_RETRIES;
  68. private boolean skipExisting = false;
  69. private boolean httpUseCaches = true; // on by default
  70. private Mapper mapperElement = null;
  71. /**
  72. * Does the work.
  73. *
  74. * @exception BuildException Thrown in unrecoverable error.
  75. */
  76. public void execute() throws BuildException {
  77. checkAttributes();
  78. for (Resource r : sources) {
  79. URLProvider up = r.as(URLProvider.class);
  80. URL source = up.getURL();
  81. File dest = destination;
  82. if (destination.isDirectory()) {
  83. if (mapperElement == null) {
  84. String path = source.getPath();
  85. if (path.endsWith("/")) {
  86. path = path.substring(0, path.length() - 1);
  87. }
  88. int slash = path.lastIndexOf("/");
  89. if (slash > -1) {
  90. path = path.substring(slash + 1);
  91. }
  92. dest = new File(destination, path);
  93. } else {
  94. FileNameMapper mapper = mapperElement.getImplementation();
  95. String[] d = mapper.mapFileName(source.toString());
  96. if (d == null) {
  97. log("skipping " + r + " - mapper can't handle it",
  98. Project.MSG_WARN);
  99. continue;
  100. } else if (d.length == 0) {
  101. log("skipping " + r + " - mapper returns no file name",
  102. Project.MSG_WARN);
  103. continue;
  104. } else if (d.length > 1) {
  105. log("skipping " + r + " - mapper returns multiple file"
  106. + " names", Project.MSG_WARN);
  107. continue;
  108. }
  109. dest = new File(destination, d[0]);
  110. }
  111. }
  112. //set up logging
  113. int logLevel = Project.MSG_INFO;
  114. DownloadProgress progress = null;
  115. if (verbose) {
  116. progress = new VerboseProgress(System.out);
  117. }
  118. //execute the get
  119. try {
  120. doGet(source, dest, logLevel, progress);
  121. } catch (IOException ioe) {
  122. log("Error getting " + source + " to " + dest);
  123. if (!ignoreErrors) {
  124. throw new BuildException(ioe, getLocation());
  125. }
  126. }
  127. }
  128. }
  129. /**
  130. * make a get request, with the supplied progress and logging info.
  131. * All the other config parameters are set at the task level,
  132. * source, dest, ignoreErrors, etc.
  133. * @param logLevel level to log at, see {@link Project#log(String, int)}
  134. * @param progress progress callback; null for no-callbacks
  135. * @return true for a successful download, false otherwise.
  136. * The return value is only relevant when {@link #ignoreErrors} is true, as
  137. * when false all failures raise BuildExceptions.
  138. * @throws IOException for network trouble
  139. * @throws BuildException for argument errors, or other trouble when ignoreErrors
  140. * is false.
  141. * @deprecated only gets the first configured resource
  142. */
  143. public boolean doGet(int logLevel, DownloadProgress progress)
  144. throws IOException {
  145. checkAttributes();
  146. for (Resource r : sources) {
  147. URLProvider up = r.as(URLProvider.class);
  148. URL source = up.getURL();
  149. return doGet(source, destination, logLevel, progress);
  150. }
  151. /*NOTREACHED*/
  152. return false;
  153. }
  154. /**
  155. * make a get request, with the supplied progress and logging info.
  156. *
  157. * All the other config parameters like ignoreErrors are set at
  158. * the task level.
  159. * @param source the URL to get
  160. * @param dest the target file
  161. * @param logLevel level to log at, see {@link Project#log(String, int)}
  162. * @param progress progress callback; null for no-callbacks
  163. * @return true for a successful download, false otherwise.
  164. * The return value is only relevant when {@link #ignoreErrors} is true, as
  165. * when false all failures raise BuildExceptions.
  166. * @throws IOException for network trouble
  167. * @throws BuildException for argument errors, or other trouble when ignoreErrors
  168. * is false.
  169. * @since Ant 1.8.0
  170. */
  171. public boolean doGet(URL source, File dest, int logLevel,
  172. DownloadProgress progress)
  173. throws IOException {
  174. if (dest.exists() && skipExisting) {
  175. log("Destination already exists (skipping): "
  176. + dest.getAbsolutePath(), logLevel);
  177. return true;
  178. }
  179. //dont do any progress, unless asked
  180. if (progress == null) {
  181. progress = new NullProgress();
  182. }
  183. log("Getting: " + source, logLevel);
  184. log("To: " + dest.getAbsolutePath(), logLevel);
  185. //set the timestamp to the file date.
  186. long timestamp = 0;
  187. boolean hasTimestamp = false;
  188. if (useTimestamp && dest.exists()) {
  189. timestamp = dest.lastModified();
  190. if (verbose) {
  191. Date t = new Date(timestamp);
  192. log("local file date : " + t.toString(), logLevel);
  193. }
  194. hasTimestamp = true;
  195. }
  196. GetThread getThread = new GetThread(source, dest,
  197. hasTimestamp, timestamp, progress,
  198. logLevel);
  199. getThread.setDaemon(true);
  200. getProject().registerThreadTask(getThread, this);
  201. getThread.start();
  202. try {
  203. getThread.join(maxTime * 1000);
  204. } catch (InterruptedException ie) {
  205. log("interrupted waiting for GET to finish",
  206. Project.MSG_VERBOSE);
  207. }
  208. if (getThread.isAlive()) {
  209. String msg = "The GET operation took longer than " + maxTime
  210. + " seconds, stopping it.";
  211. if (ignoreErrors) {
  212. log(msg);
  213. }
  214. getThread.closeStreams();
  215. if (!ignoreErrors) {
  216. throw new BuildException(msg);
  217. }
  218. return false;
  219. }
  220. return getThread.wasSuccessful();
  221. }
  222. /**
  223. * Check the attributes.
  224. */
  225. private void checkAttributes() {
  226. if (sources.size() == 0) {
  227. throw new BuildException("at least one source is required",
  228. getLocation());
  229. }
  230. for (Resource r : sources) {
  231. URLProvider up = r.as(URLProvider.class);
  232. if (up == null) {
  233. throw new BuildException("Only URLProvider resources are"
  234. + " supported", getLocation());
  235. }
  236. }
  237. if (destination == null) {
  238. throw new BuildException("dest attribute is required", getLocation());
  239. }
  240. if (destination.exists() && sources.size() > 1
  241. && !destination.isDirectory()) {
  242. throw new BuildException("The specified destination is not a"
  243. + " directory",
  244. getLocation());
  245. }
  246. if (destination.exists() && !destination.canWrite()) {
  247. throw new BuildException("Can't write to "
  248. + destination.getAbsolutePath(),
  249. getLocation());
  250. }
  251. if (sources.size() > 1 && !destination.exists()) {
  252. destination.mkdirs();
  253. }
  254. }
  255. /**
  256. * Set an URL to get.
  257. *
  258. * @param u URL for the file.
  259. */
  260. public void setSrc(URL u) {
  261. add(new URLResource(u));
  262. }
  263. /**
  264. * Adds URLs to get.
  265. * @since Ant 1.8.0
  266. */
  267. public void add(ResourceCollection rc) {
  268. sources.add(rc);
  269. }
  270. /**
  271. * Where to copy the source file.
  272. *
  273. * @param dest Path to file.
  274. */
  275. public void setDest(File dest) {
  276. this.destination = dest;
  277. }
  278. /**
  279. * If true, show verbose progress information.
  280. *
  281. * @param v if "true" then be verbose
  282. */
  283. public void setVerbose(boolean v) {
  284. verbose = v;
  285. }
  286. /**
  287. * If true, log errors but do not treat as fatal.
  288. *
  289. * @param v if "true" then don't report download errors up to ant
  290. */
  291. public void setIgnoreErrors(boolean v) {
  292. ignoreErrors = v;
  293. }
  294. /**
  295. * If true, conditionally download a file based on the timestamp
  296. * of the local copy.
  297. *
  298. * <p>In this situation, the if-modified-since header is set so
  299. * that the file is only fetched if it is newer than the local
  300. * file (or there is no local file) This flag is only valid on
  301. * HTTP connections, it is ignored in other cases. When the flag
  302. * is set, the local copy of the downloaded file will also have
  303. * its timestamp set to the remote file time.</p>
  304. *
  305. * <p>Note that remote files of date 1/1/1970 (GMT) are treated as
  306. * 'no timestamp', and web servers often serve files with a
  307. * timestamp in the future by replacing their timestamp with that
  308. * of the current time. Also, inter-computer clock differences can
  309. * cause no end of grief.</p>
  310. * @param v "true" to enable file time fetching
  311. */
  312. public void setUseTimestamp(boolean v) {
  313. useTimestamp = v;
  314. }
  315. /**
  316. * Username for basic auth.
  317. *
  318. * @param u username for authentication
  319. */
  320. public void setUsername(String u) {
  321. this.uname = u;
  322. }
  323. /**
  324. * password for the basic authentication.
  325. *
  326. * @param p password for authentication
  327. */
  328. public void setPassword(String p) {
  329. this.pword = p;
  330. }
  331. /**
  332. * The time in seconds the download is allowed to take before
  333. * being terminated.
  334. *
  335. * @since Ant 1.8.0
  336. */
  337. public void setMaxTime(long maxTime) {
  338. this.maxTime = maxTime;
  339. }
  340. /**
  341. * The number of retries to attempt upon error, defaults to 3.
  342. *
  343. * @param r retry count
  344. *
  345. * @since Ant 1.8.0
  346. */
  347. public void setRetries(int r) {
  348. this.numberRetries = r;
  349. }
  350. /**
  351. * Skip files that already exist locally.
  352. *
  353. * @param s "true" to skip existing destination files
  354. *
  355. * @since Ant 1.8.0
  356. */
  357. public void setSkipExisting(boolean s) {
  358. this.skipExisting = s;
  359. }
  360. /**
  361. * HTTP connections only - control caching on the
  362. * HttpUrlConnection: httpConnection.setUseCaches(); if false, do
  363. * not allow caching on the HttpUrlConnection.
  364. *
  365. * <p>Defaults to true (allow caching, which is also the
  366. * HttpUrlConnection default value.</p>
  367. *
  368. * @since Ant 1.8.0
  369. */
  370. public void setHttpUseCaches(boolean httpUseCache) {
  371. this.httpUseCaches = httpUseCache;
  372. }
  373. /**
  374. * Define the mapper to map source to destination files.
  375. * @return a mapper to be configured.
  376. * @exception BuildException if more than one mapper is defined.
  377. * @since Ant 1.8.0
  378. */
  379. public Mapper createMapper() throws BuildException {
  380. if (mapperElement != null) {
  381. throw new BuildException("Cannot define more than one mapper",
  382. getLocation());
  383. }
  384. mapperElement = new Mapper(getProject());
  385. return mapperElement;
  386. }
  387. /**
  388. * Add a nested filenamemapper.
  389. * @param fileNameMapper the mapper to add.
  390. * @since Ant 1.8.0
  391. */
  392. public void add(FileNameMapper fileNameMapper) {
  393. createMapper().add(fileNameMapper);
  394. }
  395. /**
  396. * Provide this for Backward Compatibility.
  397. */
  398. protected static class Base64Converter
  399. extends org.apache.tools.ant.util.Base64Converter {
  400. }
  401. /**
  402. * Interface implemented for reporting
  403. * progess of downloading.
  404. */
  405. public interface DownloadProgress {
  406. /**
  407. * begin a download
  408. */
  409. void beginDownload();
  410. /**
  411. * tick handler
  412. *
  413. */
  414. void onTick();
  415. /**
  416. * end a download
  417. */
  418. void endDownload();
  419. }
  420. /**
  421. * do nothing with progress info
  422. */
  423. public static class NullProgress implements DownloadProgress {
  424. /**
  425. * begin a download
  426. */
  427. public void beginDownload() {
  428. }
  429. /**
  430. * tick handler
  431. *
  432. */
  433. public void onTick() {
  434. }
  435. /**
  436. * end a download
  437. */
  438. public void endDownload() {
  439. }
  440. }
  441. /**
  442. * verbose progress system prints to some output stream
  443. */
  444. public static class VerboseProgress implements DownloadProgress {
  445. private int dots = 0;
  446. // CheckStyle:VisibilityModifier OFF - bc
  447. PrintStream out;
  448. // CheckStyle:VisibilityModifier ON
  449. /**
  450. * Construct a verbose progress reporter.
  451. * @param out the output stream.
  452. */
  453. public VerboseProgress(PrintStream out) {
  454. this.out = out;
  455. }
  456. /**
  457. * begin a download
  458. */
  459. public void beginDownload() {
  460. dots = 0;
  461. }
  462. /**
  463. * tick handler
  464. *
  465. */
  466. public void onTick() {
  467. out.print(".");
  468. if (dots++ > DOTS_PER_LINE) {
  469. out.flush();
  470. dots = 0;
  471. }
  472. }
  473. /**
  474. * end a download
  475. */
  476. public void endDownload() {
  477. out.println();
  478. out.flush();
  479. }
  480. }
  481. private class GetThread extends Thread {
  482. private final URL source;
  483. private final File dest;
  484. private final boolean hasTimestamp;
  485. private final long timestamp;
  486. private final DownloadProgress progress;
  487. private final int logLevel;
  488. private boolean success = false;
  489. private IOException ioexception = null;
  490. private BuildException exception = null;
  491. private InputStream is = null;
  492. private OutputStream os = null;
  493. private URLConnection connection;
  494. private int redirections = 0;
  495. GetThread(URL source, File dest,
  496. boolean h, long t, DownloadProgress p, int l) {
  497. this.source = source;
  498. this.dest = dest;
  499. hasTimestamp = h;
  500. timestamp = t;
  501. progress = p;
  502. logLevel = l;
  503. }
  504. public void run() {
  505. try {
  506. success = get();
  507. } catch (IOException ioex) {
  508. ioexception = ioex;
  509. } catch (BuildException bex) {
  510. exception = bex;
  511. }
  512. }
  513. private boolean get() throws IOException, BuildException {
  514. connection = openConnection(source);
  515. if (connection == null)
  516. {
  517. return false;
  518. }
  519. boolean downloadSucceeded = downloadFile();
  520. //if (and only if) the use file time option is set, then
  521. //the saved file now has its timestamp set to that of the
  522. //downloaded file
  523. if (downloadSucceeded && useTimestamp) {
  524. updateTimeStamp();
  525. }
  526. return downloadSucceeded;
  527. }
  528. private boolean redirectionAllowed(URL aSource, URL aDest) {
  529. if (!(aSource.getProtocol().equals(aDest.getProtocol()) || (HTTP
  530. .equals(aSource.getProtocol()) && HTTPS.equals(aDest
  531. .getProtocol())))) {
  532. String message = "Redirection detected from "
  533. + aSource.getProtocol() + " to " + aDest.getProtocol()
  534. + ". Protocol switch unsafe, not allowed.";
  535. if (ignoreErrors) {
  536. log(message, logLevel);
  537. return false;
  538. } else {
  539. throw new BuildException(message);
  540. }
  541. }
  542. redirections++;
  543. if (redirections > REDIRECT_LIMIT) {
  544. String message = "More than " + REDIRECT_LIMIT
  545. + " times redirected, giving up";
  546. if (ignoreErrors) {
  547. log(message, logLevel);
  548. return false;
  549. } else {
  550. throw new BuildException(message);
  551. }
  552. }
  553. return true;
  554. }
  555. private URLConnection openConnection(URL aSource) throws IOException {
  556. // set up the URL connection
  557. URLConnection connection = aSource.openConnection();
  558. // modify the headers
  559. // NB: things like user authentication could go in here too.
  560. if (hasTimestamp) {
  561. connection.setIfModifiedSince(timestamp);
  562. }
  563. // prepare Java 1.1 style credentials
  564. if (uname != null || pword != null) {
  565. String up = uname + ":" + pword;
  566. String encoding;
  567. // we do not use the sun impl for portability,
  568. // and always use our own implementation for consistent
  569. // testing
  570. Base64Converter encoder = new Base64Converter();
  571. encoding = encoder.encode(up.getBytes());
  572. connection.setRequestProperty("Authorization", "Basic "
  573. + encoding);
  574. }
  575. if (connection instanceof HttpURLConnection) {
  576. ((HttpURLConnection) connection)
  577. .setInstanceFollowRedirects(false);
  578. ((HttpURLConnection) connection)
  579. .setUseCaches(httpUseCaches);
  580. }
  581. // connect to the remote site (may take some time)
  582. try {
  583. connection.connect();
  584. } catch (NullPointerException e) {
  585. //bad URLs can trigger NPEs in some JVMs
  586. throw new BuildException("Failed to parse " + source.toString(), e);
  587. }
  588. // First check on a 301 / 302 (moved) response (HTTP only)
  589. if (connection instanceof HttpURLConnection) {
  590. HttpURLConnection httpConnection = (HttpURLConnection) connection;
  591. int responseCode = httpConnection.getResponseCode();
  592. if (responseCode == HttpURLConnection.HTTP_MOVED_PERM ||
  593. responseCode == HttpURLConnection.HTTP_MOVED_TEMP ||
  594. responseCode == HttpURLConnection.HTTP_SEE_OTHER)
  595. {
  596. String newLocation = httpConnection.getHeaderField("Location");
  597. String message = aSource
  598. + (responseCode == HttpURLConnection.HTTP_MOVED_PERM ? " permanently"
  599. : "") + " moved to " + newLocation;
  600. log(message, logLevel);
  601. URL newURL = new URL(aSource, newLocation);
  602. if (!redirectionAllowed(aSource, newURL))
  603. {
  604. return null;
  605. }
  606. return openConnection(newURL);
  607. }
  608. // next test for a 304 result (HTTP only)
  609. long lastModified = httpConnection.getLastModified();
  610. if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED
  611. || (lastModified != 0 && hasTimestamp && timestamp >= lastModified)) {
  612. // not modified so no file download. just return
  613. // instead and trace out something so the user
  614. // doesn't think that the download happened when it
  615. // didn't
  616. log("Not modified - so not downloaded", logLevel);
  617. return null;
  618. }
  619. // test for 401 result (HTTP only)
  620. if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
  621. String message = "HTTP Authorization failure";
  622. if (ignoreErrors) {
  623. log(message, logLevel);
  624. return null;
  625. } else {
  626. throw new BuildException(message);
  627. }
  628. }
  629. }
  630. //REVISIT: at this point even non HTTP connections may
  631. //support the if-modified-since behaviour -we just check
  632. //the date of the content and skip the write if it is not
  633. //newer. Some protocols (FTP) don't include dates, of
  634. //course.
  635. return connection;
  636. }
  637. private boolean downloadFile()
  638. throws FileNotFoundException, IOException {
  639. for (int i = 0; i < numberRetries; i++) {
  640. // this three attempt trick is to get round quirks in different
  641. // Java implementations. Some of them take a few goes to bind
  642. // property; we ignore the first couple of such failures.
  643. try {
  644. is = connection.getInputStream();
  645. break;
  646. } catch (IOException ex) {
  647. log("Error opening connection " + ex, logLevel);
  648. }
  649. }
  650. if (is == null) {
  651. log("Can't get " + source + " to " + dest, logLevel);
  652. if (ignoreErrors) {
  653. return false;
  654. }
  655. throw new BuildException("Can't get " + source + " to " + dest,
  656. getLocation());
  657. }
  658. os = new FileOutputStream(dest);
  659. progress.beginDownload();
  660. boolean finished = false;
  661. try {
  662. byte[] buffer = new byte[BIG_BUFFER_SIZE];
  663. int length;
  664. while (!isInterrupted() && (length = is.read(buffer)) >= 0) {
  665. os.write(buffer, 0, length);
  666. progress.onTick();
  667. }
  668. finished = !isInterrupted();
  669. } finally {
  670. FileUtils.close(os);
  671. FileUtils.close(is);
  672. // we have started to (over)write dest, but failed.
  673. // Try to delete the garbage we'd otherwise leave
  674. // behind.
  675. if (!finished) {
  676. dest.delete();
  677. }
  678. }
  679. progress.endDownload();
  680. return true;
  681. }
  682. private void updateTimeStamp() {
  683. long remoteTimestamp = connection.getLastModified();
  684. if (verbose) {
  685. Date t = new Date(remoteTimestamp);
  686. log("last modified = " + t.toString()
  687. + ((remoteTimestamp == 0)
  688. ? " - using current time instead"
  689. : ""), logLevel);
  690. }
  691. if (remoteTimestamp != 0) {
  692. FILE_UTILS.setFileLastModified(dest, remoteTimestamp);
  693. }
  694. }
  695. /**
  696. * Has the download completed successfully?
  697. *
  698. * <p>Re-throws any exception caught during executaion.</p>
  699. */
  700. boolean wasSuccessful() throws IOException, BuildException {
  701. if (ioexception != null) {
  702. throw ioexception;
  703. }
  704. if (exception != null) {
  705. throw exception;
  706. }
  707. return success;
  708. }
  709. /**
  710. * Closes streams, interrupts the download, may delete the
  711. * output file.
  712. */
  713. void closeStreams() {
  714. interrupt();
  715. FileUtils.close(os);
  716. FileUtils.close(is);
  717. if (!success && dest.exists()) {
  718. dest.delete();
  719. }
  720. }
  721. }
  722. }