What is the best way to calculate a checksum for a Java class?

I have an application where I am generating a "target file" based on a Java "source" class. I want to regenerate the target when the source changes. I have decided the best way to do this would be to get a byte[] of the class contents and calculate a checksum on the byte[].

I am looking for the best way to get the byte[] for a class. This byte[] would be equivalent to the contents of the compiled .class file. Using ObjectOutputStream does not work. The code below generates a byte[] that is much smaller than the byte contents of the class file.

// Incorrect function to calculate the byte[] contents of a Java class
public static final byte[] getClassContents(Class<?> myClass) throws IOException {
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    try( ObjectOutputStream stream = new ObjectOutputStream(buffer) ) {
        stream.writeObject(myClass);
    }
    // This byte array is much smaller than the contents of the *.class file!!!
    byte[] contents = buffer.toByteArray();
    return contents;
}

Is there a way to get the byte[] with the identical contents of the *.class file? Calculating the checksum is the easy part, the hard part is obtaining the byte[] contents used to calculate an MD5 or CRC32 checksum.

Answers


THis is the solution that I ended up using. I don't know if it's the most efficient implementation, but the following code uses the class loader to get the location of the *.class file and reads its contents. For simplicity, I skipped buffering of the read.

// Function to obtain the byte[] contents of a Java class
public static final byte[] getClassContents(Class<?> myClass) throws IOException {
    String path = myClass.getName().replace('.', '/');
    String fileName = new StringBuffer(path).append(".class").toString();
    URL url = myClass.getClassLoader().getResource(fileName);
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    try (InputStream stream = url.openConnection().getInputStream()) {
        int datum = stream.read();
        while( datum != -1) {
            buffer.write(datum);
            datum = stream.read();
        }
    }   
    return buffer.toByteArray();
}

I don't get what you means, but i think you are looking for this, MD5.

To check MD5 of a file, you can use this code

public String getMd5(File file)
{
    DigestInputStream stream = null;
    try
    {
        stream = new DigestInputStream(new FileInputStream(file), MessageDigest.getInstance("MD5"));
        byte[] buffer = new byte[65536];

        read = stream.read(buffer);
        while (read >= 1) {
        read = stream.read(buffer);
        }
    }
    catch (Exception ignored)
    {
        int read;
        return null;
    }
    return String.format("%1$032x", new Object[] { new BigInteger(1, stream.getMessageDigest().digest()) });
}

Then, you can store the md5 of a file in any way for exmaple XML. An exmaple of MD5 is 49e6d7e2967d1a471341335c49f46c6c so once the file name and size change, md5 will change. You can store md5 of each file in XML format and next time your run a code to check md5 and compare the md5 of each file in the xml file.


If you really want the contents of the .class file, you should read the contents of .class file, not the byte[] representation that is in memory. So something like

import java.io.*;

public class ReadSelf {
    public static void main(String args[]) throws Exception  {
        Class classInstance = ReadSelf.class;
        byte[] bytes = readClass(classInstance);
    }
    public static byte[] readClass(Class classInstance) throws Exception {
        String name = classInstance.getName();
        name = name.replaceAll("[.]", "/") + ".class";
        System.out.println("Reading this: " + name);
        File file = new File(name);
        System.out.println("exists: " + file.exists());
        return read(file);
    }
    public static byte[] read(File file) throws Exception {
        byte[] data = new byte[(int)file.length()]; // can only read a file of size INT_MAX
        DataInputStream inputStream =
        new DataInputStream(
            new BufferedInputStream(
                new FileInputStream(file)));

        int total = 0;
        int nRead = 0;
        try {
            while((nRead = inputStream.read(data)) != -1) {
                total += nRead;
            }
        }
        finally {
            inputStream.close();
        }
        System.out.println("Read " + total
            + " characters, which should match file length of "
            + file.length() + " characters");
        return data;
    }
}

Need Your Help

Unable to Export the Dynamically added row to Gridview to Excel

c# asp.net export-to-excel

I have tried Exporting GridView to Excel but observed that