Question

I am facing some issues while serializing objects (I am using JBoss Drools, and want to store an ArrayList of KnowledgePackage).

When I serialize the list, store the result in a file, and deserialize it, no problem occurs, so it works fine.

But when I serialize the list, store the result in a byte stream, then save it in a JarFile, i cannot then deserialize the result, because of this error :

IOException during package import : java.util.ArrayList; local class incompatible: stream classdesc serialVersionUID = 8664875232659988799, local class serialVersionUID = 8683452581122892189

So I think the issue is when I am saving the serialized object into a Jarfile entry. I think I am doing this right, because other files saved the same way in the Jarfile can correctly be read. And after using 'cmp' and 'hexdump', I have spotted that saving it it an jar causes a variation of one octet if the uuid, else the content is the same.

I am really disappointed and can not state where the problem may be.

What can modify the SerialVersionUID between two classes ? other than another vm version ?


adding source code : exportToJar -> writeRulesPackageEntry -> writeEntry

/**
 * Writes content provided from a reader into a file contained in a jar.
 * 
 * @param output the output stream to write on
 * @param entryName the name of the file that will contain reader data
 * @param contentReader 
 * 
 * @return the zip entry that has been created into the jar
 */
ZipEntry writeEntry(JarOutputStream output, String entryName, ByteArrayInputStream input) {
    if (output == null || entryName == null || entryName.trim().length() == 0 || input == null) {
        throw new NullPointerException("Null argument passed");
    }

    ZipEntry entry = new ZipEntry(entryName);
    byte[] buffer = new byte[BUFFER_LENGTH];

    try {
        output.putNextEntry(entry);
        int nRead;

        while ((nRead = input.read(buffer, 0, BUFFER_LENGTH)) > 0) {
            output.write(buffer, 0, nRead);
        }

        output.closeEntry();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return entry;
}

/**
 * Export rules files to a serialized object (ArrayList<KnowledgePackage>) into 
 * an output stream, then write the output content as an entry of a jar.
 * 
 * @param os the output jar to write in
 */
void writeRulesPackageEntry(JarOutputStream os) {
    // serialize objects and write them to the output stream
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    RulesPackaging rulesPackaging = new RulesPackaging();
    rulesPackaging.exportResources(this.rules, output);

    // create a new input stream to read written objects from
    ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
    this.writeEntry(os, Product.ENTRY_RULES_PACKAGE, input);
}

/**
 * Creates a JarFile containing resources. 
 * 
 * @param filename the exported jar filename
 * @return the jar as an object, null if an error occured
 */
public JarFile exportToJar(String filename) {
    FileOutputStream fOs;
    JarOutputStream jOs;
    JarFile jar = null;

    try {
        fOs = new FileOutputStream(filename);
        jOs = new JarOutputStream(fOs);

        this.writeRulesPackageEntry(jOs);

        jOs.close();

        // construct a jar from the output jar
        jar = new JarFile(new File(filename));
    } catch (IOException e) {
        e.printStackTrace();
    }

    return jar;
}
Was it helpful?

Solution

The serialVersionUID doesn't change. It is a static final assigned at compilation time (based on a hash of the source code, I think) unless a value is assigned explicitly in the source code.

There's a bit more about it here http://mindprod.com/jgloss/serialization.html.

With the exception you are seeing the correct serialVersionUID for java.util.ArrayList is 8683452581122892189L, which is assigned explicitly in the source code and has remained the same since the class was introduced in 1.2.

As you've said the error is most likely occurring when byte stream to the JarFile - please post the code you're using to do that.

Cont'd after the source code was posted

I suspect the problem lies in the use of the java.io.InputStreamReader.

From the JavaDoc:

An InputStreamReader is a bridge from byte streams to character streams: It reads bytes and decodes them into characters using a specified charset. The charset that it uses may be specified by name or may be given explicitly, or the platform's default charset may be accepted.

As soon as I see character sets involved in non-text streams I always get suspicious because it's possible for the stream to be modified during the decoding is a sequence of bytes doesn't correspond to a character in the character set (seen those little square characters that occurs when encoding issues happen). I would try reading the bytes straight off the java.io.ByteArrayInputStream that you are wrapping with the java.io.InputStreamReader in writeRulesPackageEntry(JarOutputStream). The conversion to a char[] isn't necessary.

OTHER TIPS

Like Nick proposes, the issue is most likely that you're not treating the stream as bytes (which are never altered), but as characters (which can be).

Having said that, another decent resource on Serialization is a dedicated chapter from a book I wrote a million years ago (1997), "Mastering JavaBeans". Luckily chapter 11, Serialization, is as relevant today as it was then. Download the free PDFs from http://ccd.uab.es/~srobles/manuals/JavaBeans

Is there any chance that an earlier version was serialized into the JarFile, and subsequent serialization attempts are failing to overwrite it? Then you'd be retrieving the serialized data of an earlier version of the class, which would (correctly) throw the "incompatible" error.

The reason I ask, is that I've seen similar error messages using my caching system of choice (EHCache) when I've updated a serialized class yet haven't dropped the old persistent cache.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top