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.

Checksum.java 16 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2001-2002 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import java.io.BufferedReader;
  56. import java.io.File;
  57. import java.io.FileInputStream;
  58. import java.io.FileOutputStream;
  59. import java.io.IOException;
  60. import java.io.InputStreamReader;
  61. import java.security.DigestInputStream;
  62. import java.security.MessageDigest;
  63. import java.security.NoSuchAlgorithmException;
  64. import java.security.NoSuchProviderException;
  65. import java.util.Enumeration;
  66. import java.util.Hashtable;
  67. import java.util.Vector;
  68. import org.apache.tools.ant.BuildException;
  69. import org.apache.tools.ant.DirectoryScanner;
  70. import org.apache.tools.ant.Project;
  71. import org.apache.tools.ant.taskdefs.condition.Condition;
  72. import org.apache.tools.ant.types.FileSet;
  73. /**
  74. * Used to create or verify file checksums.
  75. *
  76. * @author <a href="mailto:umagesh@apache.org">Magesh Umasankar</a>
  77. *
  78. * @since Ant 1.5
  79. *
  80. * @ant.task category="control"
  81. */
  82. public class Checksum extends MatchingTask implements Condition {
  83. /**
  84. * File for which checksum is to be calculated.
  85. */
  86. private File file = null;
  87. /**
  88. * MessageDigest algorithm to be used.
  89. */
  90. private String algorithm = "MD5";
  91. /**
  92. * MessageDigest Algorithm provider
  93. */
  94. private String provider = null;
  95. /**
  96. * File Extension that is be to used to create or identify
  97. * destination file
  98. */
  99. private String fileext;
  100. /**
  101. * Holds generated checksum and gets set as a Project Property.
  102. */
  103. private String property;
  104. /**
  105. * Whether or not to create a new file.
  106. * Defaults to <code>false</code>.
  107. */
  108. private boolean forceOverwrite;
  109. /**
  110. * Contains the result of a checksum verification. ("true" or "false")
  111. */
  112. private String verifyProperty;
  113. /**
  114. * Vector to hold source file sets.
  115. */
  116. private Vector filesets = new Vector();
  117. /**
  118. * Stores SourceFile, DestFile pairs and SourceFile, Property String pairs.
  119. */
  120. private Hashtable includeFileMap = new Hashtable();
  121. /**
  122. * Message Digest instance
  123. */
  124. private MessageDigest messageDigest;
  125. /**
  126. * is this task being used as a nested condition element?
  127. */
  128. private boolean isCondition;
  129. /**
  130. * Size of the read buffer to use.
  131. */
  132. private int readBufferSize = 8 * 1024;
  133. /**
  134. * Sets the file for which the checksum is to be calculated.
  135. */
  136. public void setFile(File file) {
  137. this.file = file;
  138. }
  139. /**
  140. * Specifies the algorithm to be used to compute the checksum.
  141. * Defaults to "MD5". Other popular algorithms like "SHA" may be used as well.
  142. */
  143. public void setAlgorithm(String algorithm) {
  144. this.algorithm = algorithm;
  145. }
  146. /**
  147. * Sets the MessageDigest algorithm provider to be used
  148. * to calculate the checksum.
  149. */
  150. public void setProvider(String provider) {
  151. this.provider = provider;
  152. }
  153. /**
  154. * Sets the file extension that is be to used to
  155. * create or identify destination file.
  156. */
  157. public void setFileext(String fileext) {
  158. this.fileext = fileext;
  159. }
  160. /**
  161. * Sets the property to hold the generated checksum.
  162. */
  163. public void setProperty(String property) {
  164. this.property = property;
  165. }
  166. /**
  167. * Sets the verify property. This project property holds
  168. * the result of a checksum verification - "true" or "false"
  169. */
  170. public void setVerifyproperty(String verifyProperty) {
  171. this.verifyProperty = verifyProperty;
  172. }
  173. /**
  174. * Whether or not to overwrite existing file irrespective of
  175. * whether it is newer than
  176. * the source file. Defaults to false.
  177. */
  178. public void setForceOverwrite(boolean forceOverwrite) {
  179. this.forceOverwrite = forceOverwrite;
  180. }
  181. /**
  182. * The size of the read buffer to use.
  183. */
  184. public void setReadBufferSize(int size) {
  185. this.readBufferSize = size;
  186. }
  187. /**
  188. * Files to generate checksums for.
  189. */
  190. public void addFileset(FileSet set) {
  191. filesets.addElement(set);
  192. }
  193. /**
  194. * Calculate the checksum(s).
  195. */
  196. public void execute() throws BuildException {
  197. isCondition = false;
  198. boolean value = validateAndExecute();
  199. if (verifyProperty != null) {
  200. getProject().setNewProperty(verifyProperty,
  201. new Boolean(value).toString());
  202. }
  203. }
  204. /**
  205. * Calculate the checksum(s)
  206. *
  207. * @return Returns true if the checksum verification test passed,
  208. * false otherwise.
  209. */
  210. public boolean eval() throws BuildException {
  211. isCondition = true;
  212. return validateAndExecute();
  213. }
  214. /**
  215. * Validate attributes and get down to business.
  216. */
  217. private boolean validateAndExecute() throws BuildException {
  218. String savedFileExt = fileext;
  219. if (file == null && filesets.size() == 0) {
  220. throw new BuildException(
  221. "Specify at least one source - a file or a fileset.");
  222. }
  223. if (file != null && file.exists() && file.isDirectory()) {
  224. throw new BuildException(
  225. "Checksum cannot be generated for directories");
  226. }
  227. if (property != null && fileext != null) {
  228. throw new BuildException(
  229. "Property and FileExt cannot co-exist.");
  230. }
  231. if (property != null) {
  232. if (forceOverwrite) {
  233. throw new BuildException(
  234. "ForceOverwrite cannot be used when Property is specified");
  235. }
  236. if (file != null) {
  237. if (filesets.size() > 0) {
  238. throw new BuildException("Multiple files cannot be used "
  239. + "when Property is specified");
  240. }
  241. } else {
  242. if (filesets.size() > 1) {
  243. throw new BuildException("Multiple files cannot be used "
  244. + "when Property is specified");
  245. }
  246. }
  247. }
  248. if (verifyProperty != null) {
  249. isCondition = true;
  250. }
  251. if (verifyProperty != null && forceOverwrite) {
  252. throw new BuildException(
  253. "VerifyProperty and ForceOverwrite cannot co-exist.");
  254. }
  255. if (isCondition && forceOverwrite) {
  256. throw new BuildException("ForceOverwrite cannot be used when "
  257. + "conditions are being used.");
  258. }
  259. messageDigest = null;
  260. if (provider != null) {
  261. try {
  262. messageDigest = MessageDigest.getInstance(algorithm, provider);
  263. } catch (NoSuchAlgorithmException noalgo) {
  264. throw new BuildException(noalgo, getLocation());
  265. } catch (NoSuchProviderException noprovider) {
  266. throw new BuildException(noprovider, getLocation());
  267. }
  268. } else {
  269. try {
  270. messageDigest = MessageDigest.getInstance(algorithm);
  271. } catch (NoSuchAlgorithmException noalgo) {
  272. throw new BuildException(noalgo, getLocation());
  273. }
  274. }
  275. if (messageDigest == null) {
  276. throw new BuildException("Unable to create Message Digest",
  277. getLocation());
  278. }
  279. if (fileext == null) {
  280. fileext = "." + algorithm;
  281. } else if (fileext.trim().length() == 0) {
  282. throw new BuildException(
  283. "File extension when specified must not be an empty string");
  284. }
  285. try {
  286. addToIncludeFileMap(file);
  287. int sizeofFileSet = filesets.size();
  288. for (int i = 0; i < sizeofFileSet; i++) {
  289. FileSet fs = (FileSet) filesets.elementAt(i);
  290. DirectoryScanner ds = fs.getDirectoryScanner(getProject());
  291. String[] srcFiles = ds.getIncludedFiles();
  292. for (int j = 0; j < srcFiles.length; j++) {
  293. File src = new File(fs.getDir(getProject()), srcFiles[j]);
  294. addToIncludeFileMap(src);
  295. }
  296. }
  297. return generateChecksums();
  298. } finally {
  299. fileext = savedFileExt;
  300. includeFileMap.clear();
  301. }
  302. }
  303. /**
  304. * Add key-value pair to the hashtable upon which
  305. * to later operate upon.
  306. */
  307. private void addToIncludeFileMap(File file) throws BuildException {
  308. if (file != null) {
  309. if (file.exists()) {
  310. if (property == null) {
  311. File dest
  312. = new File(file.getParent(), file.getName() + fileext);
  313. if (forceOverwrite || isCondition ||
  314. (file.lastModified() > dest.lastModified())) {
  315. includeFileMap.put(file, dest);
  316. } else {
  317. log(file + " omitted as " + dest + " is up to date.",
  318. Project.MSG_VERBOSE);
  319. }
  320. } else {
  321. includeFileMap.put(file, property);
  322. }
  323. } else {
  324. String message = "Could not find file "
  325. + file.getAbsolutePath()
  326. + " to generate checksum for.";
  327. log(message);
  328. throw new BuildException(message, getLocation());
  329. }
  330. }
  331. }
  332. /**
  333. * Generate checksum(s) using the message digest created earlier.
  334. */
  335. private boolean generateChecksums() throws BuildException {
  336. boolean checksumMatches = true;
  337. FileInputStream fis = null;
  338. FileOutputStream fos = null;
  339. byte[] buf = new byte[readBufferSize];
  340. try {
  341. for (Enumeration e = includeFileMap.keys(); e.hasMoreElements();) {
  342. messageDigest.reset();
  343. File src = (File) e.nextElement();
  344. if (!isCondition) {
  345. log("Calculating " + algorithm + " checksum for " + src);
  346. }
  347. fis = new FileInputStream(src);
  348. DigestInputStream dis = new DigestInputStream(fis,
  349. messageDigest);
  350. while (dis.read(buf, 0, readBufferSize) != -1) {
  351. ;
  352. }
  353. dis.close();
  354. fis.close();
  355. fis = null;
  356. byte[] fileDigest = messageDigest.digest ();
  357. StringBuffer checksumSb = new StringBuffer();
  358. for (int i = 0; i < fileDigest.length; i++) {
  359. String hexStr = Integer.toHexString(0x00ff & fileDigest[i]);
  360. if (hexStr.length() < 2) {
  361. checksumSb.append("0");
  362. }
  363. checksumSb.append(hexStr);
  364. }
  365. String checksum = checksumSb.toString();
  366. //can either be a property name string or a file
  367. Object destination = includeFileMap.get(src);
  368. if (destination instanceof java.lang.String) {
  369. String prop = (String) destination;
  370. if (isCondition) {
  371. checksumMatches = checksum.equals(property);
  372. } else {
  373. getProject().setNewProperty(prop, checksum);
  374. }
  375. } else if (destination instanceof java.io.File) {
  376. if (isCondition) {
  377. File existingFile = (File) destination;
  378. if (existingFile.exists()) {
  379. fis = new FileInputStream(existingFile);
  380. InputStreamReader isr = new InputStreamReader(fis);
  381. BufferedReader br = new BufferedReader(isr);
  382. String suppliedChecksum = br.readLine();
  383. fis.close();
  384. fis = null;
  385. br.close();
  386. isr.close();
  387. checksumMatches =
  388. checksum.equals(suppliedChecksum);
  389. } else {
  390. checksumMatches = false;
  391. }
  392. } else {
  393. File dest = (File) destination;
  394. fos = new FileOutputStream(dest);
  395. fos.write(checksum.getBytes());
  396. fos.close();
  397. fos = null;
  398. }
  399. }
  400. }
  401. } catch (Exception e) {
  402. throw new BuildException(e, getLocation());
  403. } finally {
  404. if (fis != null) {
  405. try {
  406. fis.close();
  407. } catch (IOException e) {}
  408. }
  409. if (fos != null) {
  410. try {
  411. fos.close();
  412. } catch (IOException e) {}
  413. }
  414. }
  415. return checksumMatches;
  416. }
  417. }