ehatcher 2003/06/24 08:06:12
Modified: docs/manual/CoreTasks checksum.html
src/etc/testcases/taskdefs checksum.xml
src/main/org/apache/tools/ant/taskdefs Checksum.java
src/testcases/org/apache/tools/ant/taskdefs
ChecksumTest.java
Log:
#20767 - adding totalproperty and todir attributes to checksum task -
submitted by Aslak Hellesoy
Revision Changes Path
1.7 +18 -0 ant/docs/manual/CoreTasks/checksum.html
Index: checksum.html
===================================================================
RCS file: /home/cvs/ant/docs/manual/CoreTasks/checksum.html,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- checksum.html 9 Apr 2003 14:31:47 -0000 1.6
+++ checksum.html 24 Jun 2003 15:06:03 -0000 1.7
@@ -27,6 +27,13 @@
at least one nested fileset element.</td>
</tr>
<tr>
+ <td valign="top">todir</td>
+ <td valign="top">The root directory where checksums should be
written.</td>
+ <td valign="top" align="center">No. If not specified, checksum files
+ will be written to the same directory as the files themselves.
+ </td>
+ </tr>
+ <tr>
<td valign="top">algorithm</td>
<td valign="top">Specifies the algorithm to be used to
compute the checksum. Defaults to "MD5".
@@ -61,6 +68,17 @@
This cannot be specified when fileext is being used or when the
number of files for which checksums is to be generated is greater
than 1.
+ </td>
+ <td valign="top" align="center">No</td>
+ </tr>
+ <tr>
+ <td valign="top">totalproperty</td>
+ <td valign="top">If specified, this attribute specifies the name of
+ the property that will hold a checksum of all the checksums and
+ file paths. The individual checksums and the relative paths to
+ the files within the filesets they are defined in will be used to
+ compute this checksum. (The file separators in the paths will be
+ converted to '/' before computation to ensure platform portability).
</td>
<td valign="top" align="center">No</td>
</tr>
1.4 +37 -15 ant/src/etc/testcases/taskdefs/checksum.xml
Index: checksum.xml
===================================================================
RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/checksum.xml,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- checksum.xml 9 Apr 2003 14:23:01 -0000 1.3
+++ checksum.xml 24 Jun 2003 15:06:10 -0000 1.4
@@ -2,38 +2,44 @@
<project default="cleanup" basedir=".">
<target name="cleanup">
- <delete file="../asf-logo.gif.md5" />
+ <delete file="../asf-logo.gif.MD5" />
+ <delete>
+ <fileset dir="checksum">
+ <include name="**/*.MD5"/>
+ </fileset>
+ </delete>
+ <delete dir="checksum/checksums" />
</target>
<target name="createMd5">
- <checksum file="../asf-logo.gif" fileext=".md5" />
+ <checksum file="../asf-logo.gif" fileext=".MD5" />
</target>
<target name="setProperty">
- <checksum file="../asf-logo.gif" property="logo.md5" />
+ <checksum file="../asf-logo.gif" property="logo.MD5" />
</target>
<target name="verifyAsTask">
- <copy file="expected/asf-logo.gif.md5" todir=".." />
- <checksum file="../asf-logo.gif" fileext=".md5"
- verifyproperty="logo.md5" />
+ <copy file="expected/asf-logo.gif.MD5" todir=".." />
+ <checksum file="../asf-logo.gif" fileext=".MD5"
+ verifyproperty="logo.MD5" />
- <copy file="checksum.xml" tofile="../asf-logo.gif.md5"
+ <copy file="checksum.xml" tofile="../asf-logo.gif.MD5"
overwrite="true" />
- <checksum file="../asf-logo.gif" fileext=".md5"
- verifyproperty="no.logo.md5" />
+ <checksum file="../asf-logo.gif" fileext=".MD5"
+ verifyproperty="no.logo.MD5" />
</target>
<target name="verifyAsCondition">
- <copy file="expected/asf-logo.gif.md5" todir=".." />
- <condition property="logo.md5">
- <checksum file="../asf-logo.gif" fileext=".md5" />
+ <copy file="expected/asf-logo.gif.MD5" todir=".." />
+ <condition property="logo.MD5">
+ <checksum file="../asf-logo.gif" fileext=".MD5" />
</condition>
- <copy file="checksum.xml" tofile="../asf-logo.gif.md5"
+ <copy file="checksum.xml" tofile="../asf-logo.gif.MD5"
overwrite="true" />
- <condition property="no.logo.md5">
- <checksum file="../asf-logo.gif" fileext=".md5" />
+ <condition property="no.logo.MD5">
+ <checksum file="../asf-logo.gif" fileext=".MD5" />
</condition>
</target>
@@ -41,5 +47,21 @@
<checksum property="checksum" file="checksum.xml"/>
<checksum property="${checksum}" file="checksum.xml"
verifyproperty="verify"/>
+ </target>
+
+ <target name="verifyTotal">
+ <checksum totalproperty="total">
+ <fileset dir="${basedir}/checksum">
+ <exclude name="**/*.MD5"/>
+ </fileset>
+ </checksum>
+ </target>
+
+ <target name="verifyChecksumdir">
+ <checksum totalproperty="total" todir="${basedir}/checksum/checksums">
+ <fileset dir="${basedir}/checksum">
+ <exclude name="**/*.MD5"/>
+ </fileset>
+ </checksum>
</target>
</project>
1.30 +170 -23 ant/src/main/org/apache/tools/ant/taskdefs/Checksum.java
Index: Checksum.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/Checksum.java,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -r1.29 -r1.30
--- Checksum.java 10 Feb 2003 14:13:34 -0000 1.29
+++ Checksum.java 24 Jun 2003 15:06:11 -0000 1.30
@@ -53,19 +53,25 @@
*/
package org.apache.tools.ant.taskdefs;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
-import java.util.Enumeration;
-import java.util.Hashtable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.Arrays;
+
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
@@ -76,16 +82,26 @@
* Used to create or verify file checksums.
*
* @author Magesh Umasankar
+ * @author Aslak Hellesoy
*
* @since Ant 1.5
*
* @ant.task category="control"
*/
public class Checksum extends MatchingTask implements Condition {
+
/**
* File for which checksum is to be calculated.
*/
private File file = null;
+
+ /**
+ * Root directory in which the checksu files will be written.
+ * If not specified, the checksum files will be written
+ * in the same directory as each file.
+ */
+ private File todir;
+
/**
* MessageDigest algorithm to be used.
*/
@@ -104,6 +120,23 @@
*/
private String property;
/**
+ * Holds checksums for all files (both calculated and cached on disk).
+ * Key: java.util.File (source file)
+ * Value: java.lang.String (digest)
+ */
+ private Map allDigests = new HashMap();
+ /**
+ * Holds relative file names for all files (always with a forward slash).
+ * This is used to calculate the total hash.
+ * Key: java.util.File (source file)
+ * Value: java.lang.String (relative file name)
+ */
+ private Map relativeFilePaths = new HashMap();
+ /**
+ * Property where totalChecksum gets set.
+ */
+ private String totalproperty;
+ /**
* Whether or not to create a new file.
* Defaults to <code>false</code>.
*/
@@ -141,6 +174,14 @@
}
/**
+ * 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.
*/
@@ -172,6 +213,14 @@
}
/**
+ * 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 @@
"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 @@
}
try {
- addToIncludeFileMap(file);
-
int sizeofFileSet = filesets.size();
for (int i = 0; i < sizeofFileSet; i++) {
FileSet fs = (FileSet) filesets.elementAt(i);
@@ -318,10 +370,19 @@
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 @@
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 @@
}
}
+ 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 @@
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 @@
}
}
}
+ 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 {
@@ -444,5 +551,45 @@
}
}
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;
}
}
1.5 +29 -11
ant/src/testcases/org/apache/tools/ant/taskdefs/ChecksumTest.java
Index: ChecksumTest.java
===================================================================
RCS file:
/home/cvs/ant/src/testcases/org/apache/tools/ant/taskdefs/ChecksumTest.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- ChecksumTest.java 9 Apr 2003 14:02:23 -0000 1.4
+++ ChecksumTest.java 24 Jun 2003 15:06:12 -0000 1.5
@@ -58,9 +58,11 @@
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 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 @@
}
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"));
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]