1 18 package org.apache.tools.ant.taskdefs; 19 20 import java.security.DigestInputStream ; 21 import java.security.MessageDigest ; 22 import java.security.NoSuchAlgorithmException ; 23 import java.security.NoSuchProviderException ; 24 import java.io.File ; 25 import java.io.FileOutputStream ; 26 import java.io.FileInputStream ; 27 import java.io.FileReader ; 28 import java.io.BufferedReader ; 29 import java.io.IOException ; 30 import java.util.HashMap ; 31 import java.util.Map ; 32 import java.util.Iterator ; 33 import java.util.Hashtable ; 34 import java.util.Enumeration ; 35 import java.util.Set ; 36 import java.util.Arrays ; 37 import java.text.MessageFormat ; 38 import java.text.ParseException ; 39 40 import org.apache.tools.ant.BuildException; 41 import org.apache.tools.ant.Project; 42 import org.apache.tools.ant.taskdefs.condition.Condition; 43 import org.apache.tools.ant.types.EnumeratedAttribute; 44 import org.apache.tools.ant.types.FileSet; 45 import org.apache.tools.ant.types.ResourceCollection; 46 import org.apache.tools.ant.types.resources.Union; 47 import org.apache.tools.ant.types.resources.Restrict; 48 import org.apache.tools.ant.types.resources.FileResource; 49 import org.apache.tools.ant.types.resources.selectors.Type; 50 import org.apache.tools.ant.util.FileUtils; 51 import org.apache.tools.ant.util.StringUtils; 52 53 60 public class Checksum extends MatchingTask implements Condition { 61 private static class FileUnion extends Restrict { 62 private Union u; 63 FileUnion() { 64 u = new Union(); 65 super.add(u); 66 super.add(Type.FILE); 67 } 68 public void add(ResourceCollection rc) { 69 u.add(rc); 70 } 71 } 72 73 76 private File file = null; 77 78 83 private File todir; 84 85 88 private String algorithm = "MD5"; 89 92 private String provider = null; 93 97 private String fileext; 98 101 private String property; 102 107 private Map allDigests = new HashMap (); 108 114 private Map relativeFilePaths = new HashMap (); 115 118 private String totalproperty; 119 123 private boolean forceOverwrite; 124 127 private String verifyProperty; 128 131 private FileUnion resources = null; 132 135 private Hashtable includeFileMap = new Hashtable (); 136 139 private MessageDigest messageDigest; 140 143 private boolean isCondition; 144 147 private int readBufferSize = 8 * 1024; 148 149 152 private MessageFormat format = FormatElement.getDefault().getFormat(); 153 154 158 public void setFile(File file) { 159 this.file = file; 160 } 161 162 168 public void setTodir(File todir) { 169 this.todir = todir; 170 } 171 172 177 public void setAlgorithm(String algorithm) { 178 this.algorithm = algorithm; 179 } 180 181 186 public void setProvider(String provider) { 187 this.provider = provider; 188 } 189 190 195 public void setFileext(String fileext) { 196 this.fileext = fileext; 197 } 198 199 203 public void setProperty(String property) { 204 this.property = property; 205 } 206 207 214 public void setTotalproperty(String totalproperty) { 215 this.totalproperty = totalproperty; 216 } 217 218 223 public void setVerifyproperty(String verifyProperty) { 224 this.verifyProperty = verifyProperty; 225 } 226 227 233 public void setForceOverwrite(boolean forceOverwrite) { 234 this.forceOverwrite = forceOverwrite; 235 } 236 237 241 public void setReadBufferSize(int size) { 242 this.readBufferSize = size; 243 } 244 245 251 public void setFormat(FormatElement e) { 252 format = e.getFormat(); 253 } 254 255 263 public void setPattern(String p) { 264 format = new MessageFormat (p); 265 } 266 267 271 public void addFileset(FileSet set) { 272 add(set); 273 } 274 275 279 public void add(ResourceCollection rc) { 280 if (rc == null) { 281 return; 282 } 283 resources = (resources == null) ? new FileUnion() : resources; 284 resources.add(rc); 285 } 286 287 291 public void execute() throws BuildException { 292 isCondition = false; 293 boolean value = validateAndExecute(); 294 if (verifyProperty != null) { 295 getProject().setNewProperty( 296 verifyProperty, 297 (value ? Boolean.TRUE.toString() : Boolean.FALSE.toString())); 298 } 299 } 300 301 308 public boolean eval() throws BuildException { 309 isCondition = true; 310 return validateAndExecute(); 311 } 312 313 316 private boolean validateAndExecute() throws BuildException { 317 String savedFileExt = fileext; 318 319 if (file == null && (resources == null || resources.size() == 0)) { 320 throw new BuildException( 321 "Specify at least one source - a file or a resource collection."); 322 } 323 if (!(resources == null || resources.isFilesystemOnly())) { 324 throw new BuildException("Can only calculate checksums for file-based resources."); 325 } 326 if (file != null && file.exists() && file.isDirectory()) { 327 throw new BuildException("Checksum cannot be generated for directories"); 328 } 329 if (file != null && totalproperty != null) { 330 throw new BuildException("File and Totalproperty cannot co-exist."); 331 } 332 if (property != null && fileext != null) { 333 throw new BuildException("Property and FileExt cannot co-exist."); 334 } 335 if (property != null) { 336 if (forceOverwrite) { 337 throw new BuildException( 338 "ForceOverwrite cannot be used when Property is specified"); 339 } 340 int ct = 0; 341 if (resources != null) { 342 ct += resources.size(); 343 } 344 if (file != null) { 345 ct++; 346 } 347 if (ct > 1) { 348 throw new BuildException( 349 "Multiple files cannot be used when Property is specified"); 350 } 351 } 352 if (verifyProperty != null) { 353 isCondition = true; 354 } 355 if (verifyProperty != null && forceOverwrite) { 356 throw new BuildException("VerifyProperty and ForceOverwrite cannot co-exist."); 357 } 358 if (isCondition && forceOverwrite) { 359 throw new BuildException( 360 "ForceOverwrite cannot be used when conditions are being used."); 361 } 362 messageDigest = null; 363 if (provider != null) { 364 try { 365 messageDigest = MessageDigest.getInstance(algorithm, provider); 366 } catch (NoSuchAlgorithmException noalgo) { 367 throw new BuildException(noalgo, getLocation()); 368 } catch (NoSuchProviderException noprovider) { 369 throw new BuildException(noprovider, getLocation()); 370 } 371 } else { 372 try { 373 messageDigest = MessageDigest.getInstance(algorithm); 374 } catch (NoSuchAlgorithmException noalgo) { 375 throw new BuildException(noalgo, getLocation()); 376 } 377 } 378 if (messageDigest == null) { 379 throw new BuildException("Unable to create Message Digest", getLocation()); 380 } 381 if (fileext == null) { 382 fileext = "." + algorithm; 383 } else if (fileext.trim().length() == 0) { 384 throw new BuildException("File extension when specified must not be an empty string"); 385 } 386 try { 387 if (resources != null) { 388 for (Iterator i = resources.iterator(); i.hasNext();) { 389 FileResource fr = (FileResource) i.next(); 390 File src = fr.getFile(); 391 if (totalproperty != null || todir != null) { 392 relativeFilePaths.put(src, fr.getName().replace(File.separatorChar, '/')); 396 } 397 addToIncludeFileMap(src); 398 } 399 } 400 if (file != null) { 401 if (totalproperty != null || todir != null) { 402 relativeFilePaths.put( 403 file, file.getName().replace(File.separatorChar, '/')); 404 } 405 addToIncludeFileMap(file); 406 } 407 return generateChecksums(); 408 } finally { 409 fileext = savedFileExt; 410 includeFileMap.clear(); 411 } 412 } 413 414 418 private void addToIncludeFileMap(File file) throws BuildException { 419 if (file.exists()) { 420 if (property == null) { 421 File checksumFile = getChecksumFile(file); 422 if (forceOverwrite || isCondition 423 || (file.lastModified() > checksumFile.lastModified())) { 424 includeFileMap.put(file, checksumFile); 425 } else { 426 log(file + " omitted as " + checksumFile + " is up to date.", 427 Project.MSG_VERBOSE); 428 if (totalproperty != null) { 429 String checksum = readChecksum(checksumFile); 431 byte[] digest = decodeHex(checksum.toCharArray()); 432 allDigests.put(file, digest); 433 } 434 } 435 } else { 436 includeFileMap.put(file, property); 437 } 438 } else { 439 String message = "Could not find file " 440 + file.getAbsolutePath() 441 + " to generate checksum for."; 442 log(message); 443 throw new BuildException(message, getLocation()); 444 } 445 } 446 447 private File getChecksumFile(File file) { 448 File directory; 449 if (todir != null) { 450 String path = (String ) relativeFilePaths.get(file); 452 if (path == null) { 453 throw new BuildException( 455 "Internal error: " 456 + "relativeFilePaths could not match file" 457 + file + "\n" 458 + "please file a bug report on this"); 459 } 460 directory = new File (todir, path).getParentFile(); 461 directory.mkdirs(); 463 } else { 464 directory = file.getParentFile(); 467 } 468 File checksumFile = new File (directory, file.getName() + fileext); 469 return checksumFile; 470 } 471 472 475 private boolean generateChecksums() throws BuildException { 476 boolean checksumMatches = true; 477 FileInputStream fis = null; 478 FileOutputStream fos = null; 479 byte[] buf = new byte[readBufferSize]; 480 try { 481 for (Enumeration e = includeFileMap.keys(); e.hasMoreElements();) { 482 messageDigest.reset(); 483 File src = (File ) e.nextElement(); 484 if (!isCondition) { 485 log("Calculating " + algorithm + " checksum for " + src, Project.MSG_VERBOSE); 486 } 487 fis = new FileInputStream (src); 488 DigestInputStream dis = new DigestInputStream (fis, 489 messageDigest); 490 while (dis.read(buf, 0, readBufferSize) != -1) { 491 } 493 dis.close(); 494 fis.close(); 495 fis = null; 496 byte[] fileDigest = messageDigest.digest (); 497 if (totalproperty != null) { 498 allDigests.put(src, fileDigest); 499 } 500 String checksum = createDigestString(fileDigest); 501 Object destination = includeFileMap.get(src); 503 if (destination instanceof java.lang.String ) { 504 String prop = (String ) destination; 505 if (isCondition) { 506 checksumMatches 507 = checksumMatches && checksum.equals(property); 508 } else { 509 getProject().setNewProperty(prop, checksum); 510 } 511 } else if (destination instanceof java.io.File ) { 512 if (isCondition) { 513 File existingFile = (File ) destination; 514 if (existingFile.exists()) { 515 try { 516 String suppliedChecksum = 517 readChecksum(existingFile); 518 checksumMatches = checksumMatches 519 && checksum.equals(suppliedChecksum); 520 } catch (BuildException be) { 521 checksumMatches = false; 523 } 524 } else { 525 checksumMatches = false; 526 } 527 } else { 528 File dest = (File ) destination; 529 fos = new FileOutputStream (dest); 530 fos.write(format.format(new Object [] { 531 checksum, 532 src.getName(), 533 }).getBytes()); 534 fos.write(StringUtils.LINE_SEP.getBytes()); 535 fos.close(); 536 fos = null; 537 } 538 } 539 } 540 if (totalproperty != null) { 541 Set keys = allDigests.keySet(); 544 Object [] keyArray = keys.toArray(); 545 Arrays.sort(keyArray); 547 messageDigest.reset(); 549 for (int i = 0; i < keyArray.length; i++) { 550 File src = (File ) keyArray[i]; 551 552 byte[] digest = (byte[]) allDigests.get(src); 554 messageDigest.update(digest); 555 556 String fileName = (String ) relativeFilePaths.get(src); 558 messageDigest.update(fileName.getBytes()); 559 } 560 String totalChecksum = createDigestString(messageDigest.digest()); 561 getProject().setNewProperty(totalproperty, totalChecksum); 562 } 563 } catch (Exception e) { 564 throw new BuildException(e, getLocation()); 565 } finally { 566 FileUtils.close(fis); 567 FileUtils.close(fos); 568 } 569 return checksumMatches; 570 } 571 572 private String createDigestString(byte[] fileDigest) { 573 StringBuffer checksumSb = new StringBuffer (); 574 for (int i = 0; i < fileDigest.length; i++) { 575 String hexStr = Integer.toHexString(0x00ff & fileDigest[i]); 576 if (hexStr.length() < 2) { 577 checksumSb.append("0"); 578 } 579 checksumSb.append(hexStr); 580 } 581 return checksumSb.toString(); 582 } 583 584 596 public static byte[] decodeHex(char[] data) throws BuildException { 597 int l = data.length; 598 599 if ((l & 0x01) != 0) { 600 throw new BuildException("odd number of characters."); 601 } 602 603 byte[] out = new byte[l >> 1]; 604 605 for (int i = 0, j = 0; j < l; i++) { 607 int f = Character.digit(data[j++], 16) << 4; 608 f = f | Character.digit(data[j++], 16); 609 out[i] = (byte) (f & 0xFF); 610 } 611 612 return out; 613 } 614 615 620 private String readChecksum(File f) { 621 BufferedReader diskChecksumReader = null; 622 try { 623 diskChecksumReader = new BufferedReader (new FileReader (f)); 624 Object [] result = format.parse(diskChecksumReader.readLine()); 625 if (result == null || result.length == 0 || result[0] == null) { 626 throw new BuildException("failed to find a checksum"); 627 } 628 return (String ) result[0]; 629 } catch (IOException e) { 630 throw new BuildException("Couldn't read checksum file " + f, e); 631 } catch (ParseException e) { 632 throw new BuildException("Couldn't read checksum file " + f, e); 633 } finally { 634 FileUtils.close(diskChecksumReader); 635 } 636 } 637 638 643 public static class FormatElement extends EnumeratedAttribute { 644 private static HashMap formatMap = new HashMap (); 645 private static final String CHECKSUM = "CHECKSUM"; 646 private static final String MD5SUM = "MD5SUM"; 647 private static final String SVF = "SVF"; 648 649 static { 650 formatMap.put(CHECKSUM, new MessageFormat ("{0}")); 651 formatMap.put(MD5SUM, new MessageFormat ("{0} *{1}")); 652 formatMap.put(SVF, new MessageFormat ("MD5 ({1}) = {0}")); 653 } 654 655 656 public FormatElement() { 657 super(); 658 } 659 660 664 public static FormatElement getDefault() { 665 FormatElement e = new FormatElement(); 666 e.setValue(CHECKSUM); 667 return e; 668 } 669 670 674 public MessageFormat getFormat() { 675 return (MessageFormat ) formatMap.get(getValue()); 676 } 677 678 682 public String [] getValues() { 683 return new String [] {CHECKSUM, MD5SUM, SVF}; 684 } 685 } 686 } 687 | Popular Tags |