Previous | Next | Trail Map | Writing Java Programs | Input and Output Streams


Writing Filters for Random Access Files

Let's rewrite the example from Writing Your Own Filtered Streams so that it works on RandomAccessFiles. Because RandomAccessFile implements the DataInput and DataOutput interfaces, a side benefit is that the filtered stream will also work with other DataInput and DataOutput streams including some sequential access streams (such as DataInputStream and DataOutputStream).

The CheckedIOTest example from Writing Your Own Filtered Streams implements two filtered streams, CheckedInputStream and CheckedOutputStream, that compute a checksum as data is read from or written to the stream.

In the new example, CheckedDataOutput is a re-write of CheckedOutputStream--it computes a checksum for data writen to the stream--but it operates on DataOutput objects instead of OutputStream objects. Similarly, CheckedDataInput modifies CheckedInputStream so that it now works on DataInput objects instead of InputStream objects.

CheckedDataOutput vs. CheckedOutputStream

Let's look at how CheckedDataOutput differs from CheckedOutputStream.

The first difference between CheckedDataOutput and CheckedOutputStream is that CheckedDataOutput does not extend FilterOutputStream. Instead, it implements the DataOutput interface.

public class CheckedDataOutput implements DataOutput


Note: In the interest of keeping the example simple, the CheckedDataOutput class actually provided in this lesson is not declared to implement DataOutput. This is because the DataOutput interface specifies so many methods. However, the CheckedDataOutput class as provided in the example does implement several of DataOutput's methods to illustrate how it should work.

Next, CheckedDataOutput declares a private variable to hold a DataOutput object.

private DataOutput out;
This is the object to which data will be written.

The constructor for CheckedDataOutput is different from CheckedOutputStream's constructor: CheckedDataOutput is created on a DataOutput object rather than on an OutputStream.

public CheckedDataOutput(DataOutput out, Checksum cksum) {
    this.cksum = cksum;
    this.out = out;
}
Notice that this constructor does not call super(out) like the CheckedOutputStream constructor did. This is because CheckedDataOutput extends from Object rather than a stream class.

Those are the only modifications made to CheckedOutputStream to create a filter that works on DataOutput objects.

CheckedDataInput vs. CheckedInputStream

CheckedDataInput requires the same changes as CheckedDataOuput: In addition to these changes, the read() methods are changed as well. CheckedInputStream from the original example implements two read() methods, one for reading a single byte and one for reading a byte array. The DataInput interface has methods that implement the same functionality, but they have different names and different method signatures. Thus the read() methods in the CheckedDataInput class have new names and method signatures:
public byte readByte() throws IOException {
    byte b = in.readByte();
    cksum.update(b);
    return b;
}

public void readFully(byte[] b) throws IOException {
    in.readFully(b, 0, b.length);
    cksum.update(b, 0, b.length);
}

public void readFully(byte[] b, int off, int len) throws IOException {
    in.readFully(b, off, len);
    cksum.update(b, off, len);
}

The Main Programs

Finally, this example has two main programs to test the new filters: CheckedDITest, which runs the filters on sequential access files (DataInputStream and DataOutputStream objects), and CheckedRAFTest, which runs the filters on random access files (RandomAccessFiles).

These two main programs differ only in the type of object they open the checksum filters on. CheckedDITest creates a DataInputStream and a DataOutputStream and uses the checksum filter on those. Like this:

cis = new CheckedDataInput(new DataInputStream(
                         new FileInputStream("farrago.txt")), inChecker);
cos = new CheckedDataOutput(new DataOutputStream(
                         new FileOutputStream("outagain.txt")), outChecker);
CheckedRAFTest creates two RandomAccessFiles, one for reading and one for writing, and uses the checksum filter on those:
cis = new CheckedDataInput(new RandomAccessFile("farrago.txt", "r"), inChecker);
cos = new CheckedDataOutput(new RandomAccessFile("outagain.txt", "rw"), outChecker);

When you run either of these programs you should see the following output:

Input stream check sum: 736868089
Output stream check sum: 736868089

See Also

java.io.DataInput
java.io.DataOutput


Previous | Next | Trail Map | Writing Java Programs | Input and Output Streams