views:

396

answers:

3

Hello,

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;
}
+2  A: 

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.

Nick Holt
Source has been modified and updated, after your idea. I still get the problem. I'll try tonight to isolate more the problem, I didn't expected to have this kind of issues, I'll read more about byte and char streams before doing more hazardous things. Thanks for helping )
ipingu
Just to try an isolate the problem have you tried deserializing from the the java.io.ByteArrayInputStream?
Nick Holt
ipingu
A: 

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

Laurence Vanhelsuwe
I'll read your pdf tonight, thanks for the resource.
ipingu
A: 

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.

DreadPirateShawn
That'd certainly do it, but the class it's failing on is java.util.ArrayList, which to my knowledge has had the same serialVersionUID since it was introduced in 1.2.
Nick Holt