Sunday, September 13, 2009

Java Programming Hiccup! – Binary File Handling

I discovered something interesting in Java Programming. You may have come across output streams such as FileOutputStream, BufferedOutputStream and ObjectOutputStream when learning about binary file handling in Java. All of them have a particular method called flush(). The descriptions provided for this method for the last two output streams are as follows:

BufferedOutputStream: Flushes this buffered output stream. This forces any buffered output bytes to be written out to the underlying output stream.

ObjectOutputStream: Flushes the stream. This will write any buffered output bytes and flush through to the underlying stream.

As for FileOutputStream, the method is inherited from OutputStream, which gives this description:

Flushes this output stream and forces any buffered output bytes to be written out. The general contract of flush is that calling it is an indication that, if any bytes previously written have been buffered by the implementation of the output stream, such bytes should immediately be written to their intended destination.

The flush method of OutputStream does nothing.

My university’s lecture note gives this description for the flush method:

Flushes the output stream to force any buffered output bytes to be written to the file.

My understanding from all these, is that using the flush() method anywhere in the code would actually commit all the write actions (such as writeChar, writeUTF, etc) into the file stored in the hard disk.

Normally, all the write actions are stored in memory, and when the code calls the close() method, they are committed to the actual file in hard disk before closing this file. The flush() method promises to provide you a way to commit the write actions into the file without closing it.

This is useful when it is required for a program to (for example) accept a database record from the user and immediately store it into a file, instead of waiting for the user to finish entering all the records. I wanted to see this in action, so I went to test it out. Here is a snippet of the code:

  FileOutputStream fos = new FileOutputStream("database.dat");
  BufferedOutputStream bos = new BufferedOutputStream(fos);
  ObjectOutputStream oos = new ObjectOutputStream(bos);
  while (true) //infinite loop... see comment below
  {
    //code to accept and write database record
    //also includes condition to terminate while loop
    oos.flush(); //writes this record into file
  }
  oos.close(); //closes the file

I ran the code and watched the file changes using… no, nothing sophisticated, just Notepad2. Yea, you can set Notepad2 to appear “Always on Top” and set the “File Change Notification” to auto-reload, and you pretty much have a Watch window for the binary file. Make sure you have the binary file open in your Notepad2 though.

Disappointingly, it did not work. Every time the code encountered the flush() method, nothing happened. Seems like the underlined phrase above (“does nothing”) is correct! Well, I searched the Internet, and not many people have an answer to this problem. Some even happily use the flush method, thinking that it is committing all the write actions onto the file, not realizing their mistake.

But I stumbled upon a particular site: The Java Developers Almanac 1.4: e27. Forcing Updates to a File to the Disk. There are two interesting things here: The FileDescriptor and its sync() method. The descriptions given are:

XOutputStream.flush() method: Flush the data from the streams and writers into system buffers. The data may or may not be written to disk.

FileDescriptor.sync() method: Block until the system buffers have been written to disk. After this method returns, the data is guaranteed to have been written to disk.

Anyway I decided to try them out in my code. Here’s the snippet of my modified code. Notice the two new additions.

  FileOutputStream fos = new FileOutputStream("database.dat");
  BufferedOutputStream bos = new BufferedOutputStream(fos);
  ObjectOutputStream oos = new ObjectOutputStream(bos);
  FileDescriptor fd = fos.getFD(); //FileOutputStream only!
  while (true) //infinite loop... see comment below
  {
    //code to accept and write database record
    //also includes condition to terminate while loop
    oos.flush(); fd.sync(); //writes this record into file
  }
  oos.close(); //closes the file

I tested it out, and it works! The program now commits the record into the file in hard disk before accepting the next record. Just as I want it to.

No comments:

Post a Comment

Comments are moderated, and are usually posted within 24 hours if approved. You must have a minimum of OpenID to post comments.

LinkWithin

Related Posts with Thumbnails