Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Appending to an ObjectOutputStream

Is it not possible to append to an ObjectOutputStream?

I am trying to append to a list of objects. Following snippet is a function that is called whenever a job is finished.

FileOutputStream fos = new FileOutputStream
           (preferences.getAppDataLocation() + "history" , true);
ObjectOutputStream out = new ObjectOutputStream(fos);

out.writeObject( new Stuff(stuff) );
out.close();

But when I try to read it I only get the first in the file. Then I get java.io.StreamCorruptedException.

To read I am using

FileInputStream fis = new FileInputStream
        ( preferences.getAppDataLocation() + "history");
ObjectInputStream in = new ObjectInputStream(fis);    

try{
    while(true)
        history.add((Stuff) in.readObject());
}catch( Exception e ) { 
    System.out.println( e.toString() );
}

I do not know how many objects will be present so I am reading while there are no exceptions. From what Google says this is not possible. I was wondering if anyone knows a way?

like image 885
Hamza Yerlikaya Avatar asked Jul 28 '09 14:07

Hamza Yerlikaya


People also ask

How to append ObjectOutputStream in Java?

FileOutputStream fos = new FileOutputStream (preferences. getAppDataLocation() + "history" , true); ObjectOutputStream out = new ObjectOutputStream(fos); out. writeObject( new Stuff(stuff) ); out. close();

Do you need to close ObjectOutputStream?

If you don't use it anymore, then you should definitely close it as soon as possible.

How does ObjectOutputStream work in Java?

An ObjectOutputStream writes primitive data types and graphs of Java objects to an OutputStream. The objects can be read (reconstituted) using an ObjectInputStream. Persistent storage of objects can be accomplished by using a file for the stream.

What does ObjectOutputStream Reset do?

reset() method will disregard the state of any objects already written to the stream. The state is reset to be the same as a new ObjectOutputStream. The current point in the stream is marked as reset so the corresponding ObjectInputStream will be reset at the same point.


3 Answers

Here's the trick: subclass ObjectOutputStream and override the writeStreamHeader method:

public class AppendingObjectOutputStream extends ObjectOutputStream {

  public AppendingObjectOutputStream(OutputStream out) throws IOException {
    super(out);
  }

  @Override
  protected void writeStreamHeader() throws IOException {
    // do not write a header, but reset:
    // this line added after another question
    // showed a problem with the original
    reset();
  }

}

To use it, just check whether the history file exists or not and instantiate either this appendable stream (in case the file exists = we append = we don't want a header) or the original stream (in case the file does not exist = we need a header).

Edit

I wasn't happy with the first naming of the class. This one's better: it describes the 'what it's for' rather then the 'how it's done'

Edit

Changed the name once more, to clarify, that this stream is only for appending to an existing file. It can't be used to create a new file with object data.

Edit

Added a call to reset() after this question showed that the original version that just overrode writeStreamHeader to be a no-op could under some conditions create a stream that couldn't be read.

like image 62
Andreas Dolk Avatar answered Oct 16 '22 16:10

Andreas Dolk


As the API says, the ObjectOutputStream constructor writes the serialization stream header to the underlying stream. And this header is expected to be only once, in the beginning of the file. So calling

new ObjectOutputStream(fos);

multiple times on the FileOutputStream that refers to the same file will write the header multiple times and corrupt the file.

like image 30
Tadeusz Kopec for Ukraine Avatar answered Oct 16 '22 16:10

Tadeusz Kopec for Ukraine


Because of the precise format of the serialized file, appending will indeed corrupt it. You have to write all objects to the file as part of the same stream, or else it will crash when it reads the stream metadata when it's expecting an object.

You could read the Serialization Specification for more details, or (easier) read this thread where Roedy Green says basically what I just said.

like image 8
Michael Myers Avatar answered Oct 16 '22 16:10

Michael Myers