The Java TM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search

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

| Start of Tutorial > Start of Trail > Start of Lesson | Search