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 30 kB

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