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


Writing Filters for RandomAccessFiles and DataInput/DataOutput

[PENDING: ask dac about rewriting this example]

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.

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

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 but rather implements the DataOutput interface instead.

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. CheckedDataOutput is said to wrap the DataOutput object--and each of CheckedDataOutput's methods wraps a call to the same method in the DataOutput object while performing the filtering function.

The constructor for the two classes is different as well. 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 inherits from Object rather than from a stream.

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

CheckedDataInput vs. CheckedInputStream

CheckedDataInput required the same changes as did CheckedDataOuput: In addition to these changes, the read() methods had to be changed as well. CheckedInputStream from the original example implement two read() methods, one for reading a single byte and one for reading a byte array. The DataInput interface also has methods for reading a single and for reading a byte array, but they have different names and different method signatures. Thus the new classes rewrote and renamed the read() methods so that they would operate on a DataInput object:
public byte readByte() throws IOException {
    byte b = in.readByte();
    cksum.update(b);
    return b;
}

public void read(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 only differ 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:

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
Note: These example programs do not close the filters or the RandomAccessFiles when it completes and it should.

See Also

java.io.DataInput
java.io.DataOutput


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