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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  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 org.apache.tools.ant.BuildException;
  20. import org.apache.tools.ant.Project;
  21. import org.apache.tools.ant.Task;
  22. import org.apache.tools.ant.util.FileUtils;
  23. import java.io.File;
  24. import java.io.FileOutputStream;
  25. import java.io.IOException;
  26. import java.io.InputStream;
  27. import java.io.PrintStream;
  28. import java.net.HttpURLConnection;
  29. import java.net.URL;
  30. import java.net.URLConnection;
  31. import java.util.Date;
  32. /**
  33. * Gets a particular file from a URL source.
  34. * Options include verbose reporting, timestamp based fetches and controlling
  35. * actions on failures. NB: access through a firewall only works if the whole
  36. * Java runtime is correctly configured.
  37. *
  38. * @since Ant 1.1
  39. *
  40. * @ant.task category="network"
  41. */
  42. public class Get extends Task {
  43. private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
  44. private URL source; // required
  45. private File dest; // required
  46. private boolean verbose = false;
  47. private boolean useTimestamp = false; //off by default
  48. private boolean ignoreErrors = false;
  49. private String uname = null;
  50. private String pword = null;
  51. /**
  52. * Does the work.
  53. *
  54. * @exception BuildException Thrown in unrecoverable error.
  55. */
  56. public void execute() throws BuildException {
  57. //set up logging
  58. int logLevel = Project.MSG_INFO;
  59. DownloadProgress progress = null;
  60. if (verbose) {
  61. progress = new VerboseProgress(System.out);
  62. }
  63. //execute the get
  64. try {
  65. doGet(logLevel, progress);
  66. } catch (IOException ioe) {
  67. log("Error getting " + source + " to " + dest);
  68. if (!ignoreErrors) {
  69. throw new BuildException(ioe, getLocation());
  70. }
  71. }
  72. }
  73. /**
  74. * make a get request, with the supplied progress and logging info.
  75. * All the other config parameters are set at the task level,
  76. * source, dest, ignoreErrors, etc.
  77. * @param logLevel level to log at, see {@link Project#log(String, int)}
  78. * @param progress progress callback; null for no-callbacks
  79. * @return true for a successful download, false otherwise.
  80. * The return value is only relevant when {@link #ignoreErrors} is true, as
  81. * when false all failures raise BuildExceptions.
  82. * @throws IOException for network trouble
  83. * @throws BuildException for argument errors, or other trouble when ignoreErrors
  84. * is false.
  85. */
  86. public boolean doGet(int logLevel, DownloadProgress progress)
  87. throws IOException {
  88. checkAttributes();
  89. //dont do any progress, unless asked
  90. if (progress == null) {
  91. progress = new NullProgress();
  92. }
  93. log("Getting: " + source, logLevel);
  94. log("To: " + dest.getAbsolutePath(), logLevel);
  95. //set the timestamp to the file date.
  96. long timestamp = 0;
  97. boolean hasTimestamp = false;
  98. if (useTimestamp && dest.exists()) {
  99. timestamp = dest.lastModified();
  100. if (verbose) {
  101. Date t = new Date(timestamp);
  102. log("local file date : " + t.toString(), logLevel);
  103. }
  104. hasTimestamp = true;
  105. }
  106. //set up the URL connection
  107. URLConnection connection = source.openConnection();
  108. //modify the headers
  109. //NB: things like user authentication could go in here too.
  110. if (hasTimestamp) {
  111. connection.setIfModifiedSince(timestamp);
  112. }
  113. // prepare Java 1.1 style credentials
  114. if (uname != null || pword != null) {
  115. String up = uname + ":" + pword;
  116. String encoding;
  117. //we do not use the sun impl for portability,
  118. //and always use our own implementation for consistent
  119. //testing
  120. Base64Converter encoder = new Base64Converter();
  121. encoding = encoder.encode(up.getBytes());
  122. connection.setRequestProperty ("Authorization",
  123. "Basic " + encoding);
  124. }
  125. //connect to the remote site (may take some time)
  126. connection.connect();
  127. //next test for a 304 result (HTTP only)
  128. if (connection instanceof HttpURLConnection) {
  129. HttpURLConnection httpConnection
  130. = (HttpURLConnection) connection;
  131. long lastModified = httpConnection.getLastModified();
  132. if (httpConnection.getResponseCode()
  133. == HttpURLConnection.HTTP_NOT_MODIFIED
  134. || (lastModified != 0 && hasTimestamp
  135. && timestamp >= lastModified)) {
  136. //not modified so no file download. just return
  137. //instead and trace out something so the user
  138. //doesn't think that the download happened when it
  139. //didn't
  140. log("Not modified - so not downloaded", logLevel);
  141. return false;
  142. }
  143. // test for 401 result (HTTP only)
  144. if (httpConnection.getResponseCode()
  145. == HttpURLConnection.HTTP_UNAUTHORIZED) {
  146. String message = "HTTP Authorization failure";
  147. if (ignoreErrors) {
  148. log(message, logLevel);
  149. return false;
  150. } else {
  151. throw new BuildException(message);
  152. }
  153. }
  154. }
  155. //REVISIT: at this point even non HTTP connections may
  156. //support the if-modified-since behaviour -we just check
  157. //the date of the content and skip the write if it is not
  158. //newer. Some protocols (FTP) don't include dates, of
  159. //course.
  160. InputStream is = null;
  161. for (int i = 0; i < 3; i++) {
  162. //this three attempt trick is to get round quirks in different
  163. //Java implementations. Some of them take a few goes to bind
  164. //property; we ignore the first couple of such failures.
  165. try {
  166. is = connection.getInputStream();
  167. break;
  168. } catch (IOException ex) {
  169. log("Error opening connection " + ex, logLevel);
  170. }
  171. }
  172. if (is == null) {
  173. log("Can't get " + source + " to " + dest, logLevel);
  174. if (ignoreErrors) {
  175. return false;
  176. }
  177. throw new BuildException("Can't get " + source + " to " + dest,
  178. getLocation());
  179. }
  180. FileOutputStream fos = new FileOutputStream(dest);
  181. progress.beginDownload();
  182. boolean finished = false;
  183. try {
  184. byte[] buffer = new byte[100 * 1024];
  185. int length;
  186. while ((length = is.read(buffer)) >= 0) {
  187. fos.write(buffer, 0, length);
  188. progress.onTick();
  189. }
  190. finished = true;
  191. } finally {
  192. FileUtils.close(fos);
  193. FileUtils.close(is);
  194. // we have started to (over)write dest, but failed.
  195. // Try to delete the garbage we'd otherwise leave
  196. // behind.
  197. if (!finished) {
  198. dest.delete();
  199. }
  200. }
  201. progress.endDownload();
  202. //if (and only if) the use file time option is set, then
  203. //the saved file now has its timestamp set to that of the
  204. //downloaded file
  205. if (useTimestamp) {
  206. long remoteTimestamp = connection.getLastModified();
  207. if (verbose) {
  208. Date t = new Date(remoteTimestamp);
  209. log("last modified = " + t.toString()
  210. + ((remoteTimestamp == 0)
  211. ? " - using current time instead"
  212. : ""), logLevel);
  213. }
  214. if (remoteTimestamp != 0) {
  215. FILE_UTILS.setFileLastModified(dest, remoteTimestamp);
  216. }
  217. }
  218. //successful download
  219. return true;
  220. }
  221. /**
  222. * Check the attributes.
  223. */
  224. private void checkAttributes() {
  225. if (source == null) {
  226. throw new BuildException("src attribute is required", getLocation());
  227. }
  228. if (dest == null) {
  229. throw new BuildException("dest attribute is required", getLocation());
  230. }
  231. if (dest.exists() && dest.isDirectory()) {
  232. throw new BuildException("The specified destination is a directory",
  233. getLocation());
  234. }
  235. if (dest.exists() && !dest.canWrite()) {
  236. throw new BuildException("Can't write to " + dest.getAbsolutePath(),
  237. getLocation());
  238. }
  239. }
  240. /**
  241. * Set the URL to get.
  242. *
  243. * @param u URL for the file.
  244. */
  245. public void setSrc(URL u) {
  246. this.source = u;
  247. }
  248. /**
  249. * Where to copy the source file.
  250. *
  251. * @param dest Path to file.
  252. */
  253. public void setDest(File dest) {
  254. this.dest = dest;
  255. }
  256. /**
  257. * If true, show verbose progress information.
  258. *
  259. * @param v if "true" then be verbose
  260. */
  261. public void setVerbose(boolean v) {
  262. verbose = v;
  263. }
  264. /**
  265. * If true, log errors but do not treat as fatal.
  266. *
  267. * @param v if "true" then don't report download errors up to ant
  268. */
  269. public void setIgnoreErrors(boolean v) {
  270. ignoreErrors = v;
  271. }
  272. /**
  273. * If true, conditionally download a file based on the timestamp
  274. * of the local copy.
  275. *
  276. * <p>In this situation, the if-modified-since header is set so
  277. * that the file is only fetched if it is newer than the local
  278. * file (or there is no local file) This flag is only valid on
  279. * HTTP connections, it is ignored in other cases. When the flag
  280. * is set, the local copy of the downloaded file will also have
  281. * its timestamp set to the remote file time.</p>
  282. *
  283. * <p>Note that remote files of date 1/1/1970 (GMT) are treated as
  284. * 'no timestamp', and web servers often serve files with a
  285. * timestamp in the future by replacing their timestamp with that
  286. * of the current time. Also, inter-computer clock differences can
  287. * cause no end of grief.</p>
  288. * @param v "true" to enable file time fetching
  289. */
  290. public void setUseTimestamp(boolean v) {
  291. useTimestamp = v;
  292. }
  293. /**
  294. * Username for basic auth.
  295. *
  296. * @param u username for authentication
  297. */
  298. public void setUsername(String u) {
  299. this.uname = u;
  300. }
  301. /**
  302. * password for the basic authentication.
  303. *
  304. * @param p password for authentication
  305. */
  306. public void setPassword(String p) {
  307. this.pword = p;
  308. }
  309. /**
  310. * Provide this for Backward Compatibility.
  311. */
  312. protected static class Base64Converter
  313. extends org.apache.tools.ant.util.Base64Converter {
  314. }
  315. /**
  316. * Interface implemented for reporting
  317. * progess of downloading.
  318. */
  319. public interface DownloadProgress {
  320. /**
  321. * begin a download
  322. */
  323. void beginDownload();
  324. /**
  325. * tick handler
  326. *
  327. */
  328. void onTick();
  329. /**
  330. * end a download
  331. */
  332. void endDownload();
  333. }
  334. /**
  335. * do nothing with progress info
  336. */
  337. public static class NullProgress implements DownloadProgress {
  338. /**
  339. * begin a download
  340. */
  341. public void beginDownload() {
  342. }
  343. /**
  344. * tick handler
  345. *
  346. */
  347. public void onTick() {
  348. }
  349. /**
  350. * end a download
  351. */
  352. public void endDownload() {
  353. }
  354. }
  355. /**
  356. * verbose progress system prints to some output stream
  357. */
  358. public static class VerboseProgress implements DownloadProgress {
  359. private int dots = 0;
  360. // CheckStyle:VisibilityModifier OFF - bc
  361. PrintStream out;
  362. // CheckStyle:VisibilityModifier ON
  363. /**
  364. * Construct a verbose progress reporter.
  365. * @param out the output stream.
  366. */
  367. public VerboseProgress(PrintStream out) {
  368. this.out = out;
  369. }
  370. /**
  371. * begin a download
  372. */
  373. public void beginDownload() {
  374. dots = 0;
  375. }
  376. /**
  377. * tick handler
  378. *
  379. */
  380. public void onTick() {
  381. out.print(".");
  382. if (dots++ > 50) {
  383. out.flush();
  384. dots = 0;
  385. }
  386. }
  387. /**
  388. * end a download
  389. */
  390. public void endDownload() {
  391. out.println();
  392. out.flush();
  393. }
  394. }
  395. }