diff --git a/docs/manual/CoreTasks/checksum.html b/docs/manual/CoreTasks/checksum.html index 0451226bf..0effc771e 100644 --- a/docs/manual/CoreTasks/checksum.html +++ b/docs/manual/CoreTasks/checksum.html @@ -26,6 +26,13 @@ perform checksum verifications.
false
.
@@ -140,6 +173,14 @@ public class Checksum extends MatchingTask implements Condition {
this.file = file;
}
+ /**
+ * Sets the root directory where checksum files will be
+ * written/read
+ */
+ public void setTodir(File todir) {
+ this.todir = todir;
+ }
+
/**
* Specifies the algorithm to be used to compute the checksum.
* Defaults to "MD5". Other popular algorithms like "SHA" may be used as well.
@@ -171,6 +212,14 @@ public class Checksum extends MatchingTask implements Condition {
this.property = property;
}
+ /**
+ * Sets the property to hold the generated total checksum
+ * for all files.
+ */
+ public void setTotalproperty(String totalproperty) {
+ this.totalproperty = totalproperty;
+ }
+
/**
* Sets the verify property. This project property holds
* the result of a checksum verification - "true" or "false"
@@ -241,6 +290,11 @@ public class Checksum extends MatchingTask implements Condition {
"Checksum cannot be generated for directories");
}
+ if (file != null && totalproperty != null) {
+ throw new BuildException(
+ "File and Totalproperty cannot co-exist.");
+ }
+
if (property != null && fileext != null) {
throw new BuildException(
"Property and FileExt cannot co-exist.");
@@ -309,8 +363,6 @@ public class Checksum extends MatchingTask implements Condition {
}
try {
- addToIncludeFileMap(file);
-
int sizeofFileSet = filesets.size();
for (int i = 0; i < sizeofFileSet; i++) {
FileSet fs = (FileSet) filesets.elementAt(i);
@@ -318,10 +370,19 @@ public class Checksum extends MatchingTask implements Condition {
String[] srcFiles = ds.getIncludedFiles();
for (int j = 0; j < srcFiles.length; j++) {
File src = new File(fs.getDir(getProject()), srcFiles[j]);
+ if (totalproperty != null) {
+ // Use '/' to calculate digest based on file name.
+ // This is required in order to get the same result
+ // on different platforms.
+ String relativePath = srcFiles[j].replace(File.separatorChar, '/');
+ relativeFilePaths.put(src, relativePath);
+ }
addToIncludeFileMap(src);
}
}
+ addToIncludeFileMap(file);
+
return generateChecksums();
} finally {
fileext = savedFileExt;
@@ -337,14 +398,25 @@ public class Checksum extends MatchingTask implements Condition {
if (file != null) {
if (file.exists()) {
if (property == null) {
- File dest
- = new File(file.getParent(), file.getName() + fileext);
+ File checksumFile = getChecksumFile(file);
if (forceOverwrite || isCondition ||
- (file.lastModified() > dest.lastModified())) {
- includeFileMap.put(file, dest);
+ (file.lastModified() > checksumFile.lastModified())) {
+ includeFileMap.put(file, checksumFile);
} else {
- log(file + " omitted as " + dest + " is up to date.",
+ log(file + " omitted as " + checksumFile + " is up to date.",
Project.MSG_VERBOSE);
+ if (totalproperty != null) {
+ // Read the checksum from disk.
+ String checksum = null;
+ try {
+ BufferedReader diskChecksumReader = new BufferedReader(new FileReader(checksumFile));
+ checksum = diskChecksumReader.readLine();
+ } catch (IOException e) {
+ throw new BuildException("Couldn't read checksum file " + checksumFile, e);
+ }
+ byte[] digest = decodeHex(checksum.toCharArray());
+ allDigests.put(file,digest );
+ }
}
} else {
includeFileMap.put(file, property);
@@ -359,6 +431,23 @@ public class Checksum extends MatchingTask implements Condition {
}
}
+ private File getChecksumFile(File file) {
+ File directory;
+ if (todir != null) {
+ // A separate directory was explicitly declared
+ String path = (String) relativeFilePaths.get(file);
+ directory = new File(todir, path).getParentFile();
+ // Create the directory, as it might not exist.
+ directory.mkdirs();
+ } else {
+ // Just use the same directory as the file itself.
+ // This directory will exist
+ directory = file.getParentFile();
+ }
+ File checksumFile = new File(directory, file.getName() + fileext);
+ return checksumFile;
+ }
+
/**
* Generate checksum(s) using the message digest created earlier.
*/
@@ -384,15 +473,10 @@ public class Checksum extends MatchingTask implements Condition {
fis.close();
fis = null;
byte[] fileDigest = messageDigest.digest ();
- StringBuffer checksumSb = new StringBuffer();
- for (int i = 0; i < fileDigest.length; i++) {
- String hexStr = Integer.toHexString(0x00ff & fileDigest[i]);
- if (hexStr.length() < 2) {
- checksumSb.append("0");
- }
- checksumSb.append(hexStr);
+ if (totalproperty != null) {
+ allDigests.put(src,fileDigest);
}
- String checksum = checksumSb.toString();
+ String checksum = createDigestString(fileDigest);
//can either be a property name string or a file
Object destination = includeFileMap.get(src);
if (destination instanceof java.lang.String) {
@@ -429,6 +513,29 @@ public class Checksum extends MatchingTask implements Condition {
}
}
}
+ if (totalproperty != null) {
+ // Calculate the total checksum
+ // Convert the keys (source files) into a sorted array.
+ Set keys = allDigests.keySet();
+ Object[] keyArray = keys.toArray();
+ // File is Comparable, so sorting is trivial
+ Arrays.sort(keyArray);
+ // Loop over the checksums and generate a total hash.
+ messageDigest.reset();
+ for (int i = 0; i < keyArray.length; i++) {
+ File src = (File) keyArray[i];
+
+ // Add the digest for the file content
+ byte[] digest = (byte[]) allDigests.get(src);
+ messageDigest.update(digest);
+
+ // Add the file path
+ String fileName = (String) relativeFilePaths.get(src);
+ messageDigest.update(fileName.getBytes());
+ }
+ String totalChecksum = createDigestString(messageDigest.digest());
+ getProject().setNewProperty(totalproperty, totalChecksum);
+ }
} catch (Exception e) {
throw new BuildException(e, getLocation());
} finally {
@@ -445,4 +552,44 @@ public class Checksum extends MatchingTask implements Condition {
}
return checksumMatches;
}
+
+ private String createDigestString(byte[] fileDigest) {
+ StringBuffer checksumSb = new StringBuffer();
+ for (int i = 0; i < fileDigest.length; i++) {
+ String hexStr = Integer.toHexString(0x00ff & fileDigest[i]);
+ if (hexStr.length() < 2) {
+ checksumSb.append("0");
+ }
+ checksumSb.append(hexStr);
+ }
+ return checksumSb.toString();
+ }
+
+ /**
+ * Converts an array of characters representing hexidecimal values into an
+ * array of bytes of those same values. The returned array will be half the
+ * length of the passed array, as it takes two characters to represent any
+ * given byte. An exception is thrown if the passed char array has an odd
+ * number of elements.
+ *
+ * NOTE: This code is copied from jakarta-commons codec.
+ */
+ public static byte[] decodeHex(char[] data) throws BuildException {
+ int l = data.length;
+
+ if ((l & 0x01) != 0) {
+ throw new BuildException("odd number of characters.");
+ }
+
+ byte[] out = new byte[l >> 1];
+
+ // two characters form the hex value.
+ for (int i = 0, j = 0; j < l; i++) {
+ int f = Character.digit(data[j++], 16) << 4;
+ f = f | Character.digit(data[j++], 16);
+ out[i] = (byte) (f & 0xFF);
+ }
+
+ return out;
+ }
}
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/ChecksumTest.java b/src/testcases/org/apache/tools/ant/taskdefs/ChecksumTest.java
index 4086566c4..1fdeef9d4 100644
--- a/src/testcases/org/apache/tools/ant/taskdefs/ChecksumTest.java
+++ b/src/testcases/org/apache/tools/ant/taskdefs/ChecksumTest.java
@@ -58,9 +58,11 @@ import org.apache.tools.ant.BuildFileTest;
import org.apache.tools.ant.util.FileUtils;
import java.io.IOException;
+import java.io.File;
/**
* @author Stefan Bodewig
+ * @author Aslak Hellesoy
* @version $Revision$
*/
public class ChecksumTest extends BuildFileTest {
@@ -80,26 +82,42 @@ public class ChecksumTest extends BuildFileTest {
public void testCreateMd5() throws IOException {
FileUtils fileUtils = FileUtils.newFileUtils();
executeTarget("createMd5");
- assertTrue(fileUtils.contentEquals(project.resolveFile("expected/asf-logo.gif.md5"),
- project.resolveFile("../asf-logo.gif.md5")));
+ assertTrue(fileUtils.contentEquals(project.resolveFile("expected/asf-logo.gif.MD5"),
+ project.resolveFile("../asf-logo.gif.MD5")));
}
public void testSetProperty() {
executeTarget("setProperty");
assertEquals("0541d3df42520911f268abc730f3afe0",
- project.getProperty("logo.md5"));
+ project.getProperty("logo.MD5"));
assertTrue(!project.resolveFile("../asf-logo.gif.MD5").exists());
}
+ public void testVerifyTotal() {
+ executeTarget("verifyTotal");
+ assertEquals("ef8f1477fcc9bf93832c1a74f629c626",
+ project.getProperty("total"));
+ }
+
+ public void testVerifyChecksumdir() {
+ executeTarget("verifyChecksumdir");
+ assertEquals("ef8f1477fcc9bf93832c1a74f629c626",
+ project.getProperty("total"));
+ File shouldExist = project.resolveFile("checksum/checksums/foo/zap/Eenie.MD5");
+ File shouldNotExist = project.resolveFile("checksum/foo/zap/Eenie.MD5");
+ assertTrue( "Checksums should be written to " + shouldExist.getAbsolutePath(), shouldExist.exists());
+ assertTrue( "Checksums should not be written to " + shouldNotExist.getAbsolutePath(), !shouldNotExist.exists());
+ }
+
public void testVerifyAsTask() {
testVerify("verifyAsTask");
- assertNotNull(project.getProperty("no.logo.md5"));
- assertEquals("false", project.getProperty("no.logo.md5"));
+ assertNotNull(project.getProperty("no.logo.MD5"));
+ assertEquals("false", project.getProperty("no.logo.MD5"));
}
public void testVerifyAsCondition() {
testVerify("verifyAsCondition");
- assertNull(project.getProperty("no.logo.md5"));
+ assertNull(project.getProperty("no.logo.MD5"));
}
public void testVerifyFromProperty() {
@@ -108,11 +126,11 @@ public class ChecksumTest extends BuildFileTest {
}
private void testVerify(String target) {
- assertNull(project.getProperty("logo.md5"));
- assertNull(project.getProperty("no.logo.md5"));
+ assertNull(project.getProperty("logo.MD5"));
+ assertNull(project.getProperty("no.logo.MD5"));
executeTarget(target);
- assertNotNull(project.getProperty("logo.md5"));
- assertEquals("true", project.getProperty("logo.md5"));
+ assertNotNull(project.getProperty("logo.MD5"));
+ assertEquals("true", project.getProperty("logo.MD5"));
}
}