FileDocCategorySizeDatePackage
Checksum.javaAPI DocApache Ant 1.7024118Wed Dec 13 06:16:22 GMT 2006org.apache.tools.ant.taskdefs

Checksum

public class Checksum extends MatchingTask implements org.apache.tools.ant.taskdefs.condition.Condition
Used to create or verify file checksums.
since
Ant 1.5
ant.task
category="control"

Fields Summary
private File
file
File for which checksum is to be calculated.
private File
todir
Root directory in which the checksum files will be written. If not specified, the checksum files will be written in the same directory as each file.
private String
algorithm
MessageDigest algorithm to be used.
private String
provider
MessageDigest Algorithm provider
private String
fileext
File Extension that is be to used to create or identify destination file
private String
property
Holds generated checksum and gets set as a Project Property.
private Map
allDigests
Holds checksums for all files (both calculated and cached on disk). Key: java.util.File (source file) Value: java.lang.String (digest)
private Map
relativeFilePaths
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 String
totalproperty
Property where totalChecksum gets set.
private boolean
forceOverwrite
Whether or not to create a new file. Defaults to false.
private String
verifyProperty
Contains the result of a checksum verification. ("true" or "false")
private FileUnion
resources
Resource Collection.
private Hashtable
includeFileMap
Stores SourceFile, DestFile pairs and SourceFile, Property String pairs.
private MessageDigest
messageDigest
Message Digest instance
private boolean
isCondition
is this task being used as a nested condition element?
private int
readBufferSize
Size of the read buffer to use.
private MessageFormat
format
Formater for the checksum file.
Constructors Summary
Methods Summary
public voidadd(org.apache.tools.ant.types.ResourceCollection rc)
Add a resource collection.

param
rc the ResourceCollection to add.

        if (rc == null) {
            return;
        }
        resources = (resources == null) ? new FileUnion() : resources;
        resources.add(rc);
    
public voidaddFileset(org.apache.tools.ant.types.FileSet set)
Files to generate checksums for.

param
set a fileset of files to generate checksums for.

        add(set);
    
private voidaddToIncludeFileMap(java.io.File file)
Add key-value pair to the hashtable upon which to later operate upon.

        if (file.exists()) {
            if (property == null) {
                File checksumFile = getChecksumFile(file);
                if (forceOverwrite || isCondition
                    || (file.lastModified() > checksumFile.lastModified())) {
                    includeFileMap.put(file, checksumFile);
                } else {
                    log(file + " omitted as " + checksumFile + " is up to date.",
                        Project.MSG_VERBOSE);
                    if (totalproperty != null) {
                        // Read the checksum from disk.
                        String checksum = readChecksum(checksumFile);
                        byte[] digest = decodeHex(checksum.toCharArray());
                        allDigests.put(file, digest);
                    }
                }
            } else {
                includeFileMap.put(file, property);
            }
        } else {
            String message = "Could not find file "
                + file.getAbsolutePath()
                + " to generate checksum for.";
            log(message);
            throw new BuildException(message, getLocation());
        }
    
private java.lang.StringcreateDigestString(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();
    
public static byte[]decodeHex(char[] data)
Converts an array of characters representing hexadecimal 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.

param
data an array of characters representing hexadecimal values
return
the converted array of bytes
throws
BuildException on error

        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;
    
public booleaneval()
Calculate the checksum(s)

return
Returns true if the checksum verification test passed, false otherwise.
throws
BuildException on error

        isCondition = true;
        return validateAndExecute();
    
public voidexecute()
Calculate the checksum(s).

throws
BuildException on error

        isCondition = false;
        boolean value = validateAndExecute();
        if (verifyProperty != null) {
            getProject().setNewProperty(
                verifyProperty,
                (value ? Boolean.TRUE.toString() : Boolean.FALSE.toString()));
        }
    
private booleangenerateChecksums()
Generate checksum(s) using the message digest created earlier.

        boolean checksumMatches = true;
        FileInputStream fis = null;
        FileOutputStream fos = null;
        byte[] buf = new byte[readBufferSize];
        try {
            for (Enumeration e = includeFileMap.keys(); e.hasMoreElements();) {
                messageDigest.reset();
                File src = (File) e.nextElement();
                if (!isCondition) {
                    log("Calculating " + algorithm + " checksum for " + src, Project.MSG_VERBOSE);
                }
                fis = new FileInputStream(src);
                DigestInputStream dis = new DigestInputStream(fis,
                                                              messageDigest);
                while (dis.read(buf, 0, readBufferSize) != -1) {
                    // Empty statement
                }
                dis.close();
                fis.close();
                fis = null;
                byte[] fileDigest = messageDigest.digest ();
                if (totalproperty != null) {
                    allDigests.put(src, fileDigest);
                }
                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) {
                    String prop = (String) destination;
                    if (isCondition) {
                        checksumMatches
                            = checksumMatches && checksum.equals(property);
                    } else {
                        getProject().setNewProperty(prop, checksum);
                    }
                } else if (destination instanceof java.io.File) {
                    if (isCondition) {
                        File existingFile = (File) destination;
                        if (existingFile.exists()) {
                            try {
                                String suppliedChecksum =
                                    readChecksum(existingFile);
                                checksumMatches = checksumMatches
                                    && checksum.equals(suppliedChecksum);
                            } catch (BuildException be) {
                                // file is on wrong format, swallow
                                checksumMatches = false;
                            }
                        } else {
                            checksumMatches = false;
                        }
                    } else {
                        File dest = (File) destination;
                        fos = new FileOutputStream(dest);
                        fos.write(format.format(new Object[] {
                                                    checksum,
                                                    src.getName(),
                                                }).getBytes());
                        fos.write(StringUtils.LINE_SEP.getBytes());
                        fos.close();
                        fos = null;
                    }
                }
            }
            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 {
            FileUtils.close(fis);
            FileUtils.close(fos);
        }
        return checksumMatches;
    
private java.io.FilegetChecksumFile(java.io.File file)

        File directory;
        if (todir != null) {
            // A separate directory was explicitly declared
            String path = (String) relativeFilePaths.get(file);
            if (path == null) {
                //bug 37386. this should not occur, but it has, once.
                throw new BuildException(
                    "Internal error: "
                    + "relativeFilePaths could not match file"
                    + file + "\n"
                    + "please file a bug report on this");
            }
            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;
    
private java.lang.StringreadChecksum(java.io.File f)
reads the checksum from a file using the specified format.

since
1.7

        BufferedReader diskChecksumReader = null;
        try {
            diskChecksumReader = new BufferedReader(new FileReader(f));
            Object[] result = format.parse(diskChecksumReader.readLine());
            if (result == null || result.length == 0 || result[0] == null) {
                throw new BuildException("failed to find a checksum");
            }
            return (String) result[0];
        } catch (IOException e) {
            throw new BuildException("Couldn't read checksum file " + f, e);
        } catch (ParseException e) {
            throw new BuildException("Couldn't read checksum file " + f, e);
        } finally {
            FileUtils.close(diskChecksumReader);
        }
    
public voidsetAlgorithm(java.lang.String algorithm)
Specifies the algorithm to be used to compute the checksum. Defaults to "MD5". Other popular algorithms like "SHA" may be used as well.

param
algorithm a String value

        this.algorithm = algorithm;
    
public voidsetFile(java.io.File file)
Sets the file for which the checksum is to be calculated.

param
file a File value


                         
        
        this.file = file;
    
public voidsetFileext(java.lang.String fileext)
Sets the file extension that is be to used to create or identify destination file.

param
fileext a String value

        this.fileext = fileext;
    
public voidsetForceOverwrite(boolean forceOverwrite)
Whether or not to overwrite existing file irrespective of whether it is newer than the source file. Defaults to false.

param
forceOverwrite a boolean value

        this.forceOverwrite = forceOverwrite;
    
public voidsetFormat(org.apache.tools.ant.taskdefs.Checksum$FormatElement e)
Select the in/output pattern via a well know format name.

param
e an enumerated value
since
1.7.0

        format = e.getFormat();
    
public voidsetPattern(java.lang.String p)
Specify the pattern to use as a MessageFormat pattern.

{0} gets replaced by the checksum, {1} by the filename.

param
p a String value
since
1.7.0

        format = new MessageFormat(p);
    
public voidsetProperty(java.lang.String property)
Sets the property to hold the generated checksum.

param
property a String value

        this.property = property;
    
public voidsetProvider(java.lang.String provider)
Sets the MessageDigest algorithm provider to be used to calculate the checksum.

param
provider a String value

        this.provider = provider;
    
public voidsetReadBufferSize(int size)
The size of the read buffer to use.

param
size an int value

        this.readBufferSize = size;
    
public voidsetTodir(java.io.File todir)
Sets the root directory where checksum files will be written/read

param
todir the directory to write to
since
Ant 1.6

        this.todir = todir;
    
public voidsetTotalproperty(java.lang.String totalproperty)
Sets the property to hold the generated total checksum for all files.

param
totalproperty a String value
since
Ant 1.6

        this.totalproperty = totalproperty;
    
public voidsetVerifyproperty(java.lang.String verifyProperty)
Sets the verify property. This project property holds the result of a checksum verification - "true" or "false"

param
verifyProperty a String value

        this.verifyProperty = verifyProperty;
    
private booleanvalidateAndExecute()
Validate attributes and get down to business.

        String savedFileExt = fileext;

        if (file == null && (resources == null || resources.size() == 0)) {
            throw new BuildException(
                "Specify at least one source - a file or a resource collection.");
        }
        if (!(resources == null || resources.isFilesystemOnly())) {
            throw new BuildException("Can only calculate checksums for file-based resources.");
        }
        if (file != null && file.exists() && file.isDirectory()) {
            throw new BuildException("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.");
        }
        if (property != null) {
            if (forceOverwrite) {
                throw new BuildException(
                    "ForceOverwrite cannot be used when Property is specified");
            }
            int ct = 0;
            if (resources != null) {
                ct += resources.size();
            }
            if (file != null) {
                ct++;
            }
            if (ct > 1) {
                throw new BuildException(
                    "Multiple files cannot be used when Property is specified");
            }
        }
        if (verifyProperty != null) {
            isCondition = true;
        }
        if (verifyProperty != null && forceOverwrite) {
            throw new BuildException("VerifyProperty and ForceOverwrite cannot co-exist.");
        }
        if (isCondition && forceOverwrite) {
            throw new BuildException(
                "ForceOverwrite cannot be used when conditions are being used.");
        }
        messageDigest = null;
        if (provider != null) {
            try {
                messageDigest = MessageDigest.getInstance(algorithm, provider);
            } catch (NoSuchAlgorithmException noalgo) {
                throw new BuildException(noalgo, getLocation());
            } catch (NoSuchProviderException noprovider) {
                throw new BuildException(noprovider, getLocation());
            }
        } else {
            try {
                messageDigest = MessageDigest.getInstance(algorithm);
            } catch (NoSuchAlgorithmException noalgo) {
                throw new BuildException(noalgo, getLocation());
            }
        }
        if (messageDigest == null) {
            throw new BuildException("Unable to create Message Digest", getLocation());
        }
        if (fileext == null) {
            fileext = "." + algorithm;
        } else if (fileext.trim().length() == 0) {
            throw new BuildException("File extension when specified must not be an empty string");
        }
        try {
            if (resources != null) {
                for (Iterator i = resources.iterator(); i.hasNext();) {
                    FileResource fr = (FileResource) i.next();
                    File src = fr.getFile();
                    if (totalproperty != null || todir != null) {
                        // Use '/' to calculate digest based on file name.
                        // This is required in order to get the same result
                        // on different platforms.
                        relativeFilePaths.put(src, fr.getName().replace(File.separatorChar, '/"));
                    }
                    addToIncludeFileMap(src);
                }
            }
            if (file != null) {
                if (totalproperty != null || todir != null) {
                    relativeFilePaths.put(
                        file, file.getName().replace(File.separatorChar, '/"));
                }
                addToIncludeFileMap(file);
            }
            return generateChecksums();
        } finally {
            fileext = savedFileExt;
            includeFileMap.clear();
        }