Trail: Essential Java Classes
|
Lesson: Reading and Writing (but no 'rithmetic)
|
|
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, 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;
}
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:
-
CheckedDataInput does not derive from FilterInputStream but implements the
DataInput interface instead.
Note: In the interest of keeping the example simple, the
CheckedDataInput class actually provided in this lesson is not declared to
implement DataInput, because the DataInput interface specifies
so many methods. However, the CheckedDataInput class as provided in the example
does implement several of DataInput's methods to illustrate how it should work.
-
CheckedDataInput declares a private variable to hold a DataInput object
which it wraps.
-
The constructor for
CheckedDataInput requires a DataInput object rather
than an InputStream.
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 objects).
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, as in the following code:
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
|