Escribir Filtros para Ficheros de Acceso Aleatorio

Reescribamos el ejemplo de Escribir nuestros propios Canales Filtrados para que trabaje con un RandomAccessFile. Como RandomAccessFile implementa los interfaces DataInput y DataOutput, un beneficio lateral es que los canales filtrados también trabajan con otros canales DataInput y DataOutput incluyendo algunos canales de acceso secuencial ( como DataInputStream y DataOutputStream).

El ejemplo CheckedIOTest de Escribir nuestros propios Canales Filtrados implementa dos canales filtrados CheckedInputStream y CheckedOutputStream, que calculan la suma de los datos leidos o escritos en un canal.

En el nuevo ejemplo, CheckedDataOutput se ha reescrito CheckedOutputStream-- calcula la suma de los datos escritos en el canal -- pero opera sobre objetos DataOutput en vez de sobre objetos OutputStream. De forma similar, CheckedDataInput modifica CheckedInputStream para que ahora trabaje sobre objetos DataInput en vez de hacerlo sobre objetos InputStream.

CheckedDataOutput contra CheckedOutputStream

Echemos un vistazo a las diferencias entre CheckedDataOutput y CheckedOutputStream.

La primera diferencia es que CheckedDataOutput no desciende de FilterOutputStream. En su lugar, implementa el interface DatOutput.

public class CheckedDataOutput implements DataOutput


Nota: Para intentar mantener el ejemplo lo más sencillo posible, la clase CheckedDataOutput realmente no está declarada para implementar el interface DataInput. Esto es así porque este interface implementa demasiados métodos. Sin embargo, la clase CheckedDataOutput como está implementada en el ejemplo, si que implementa varios métodos de DataInput para ilustrar cómo deberían trabajar.

Luego, CheckedDataOutput declara una variable privada para contener el objeto DataOutput.

private DataOutput out;
Este es el objeto donde se escribiran los datos.

El constructor de CheckedDataOutput se diferencia del de CheckedOutputStream en que el primero crea un objeto DataOutput en vez de un OutputStream.

public CheckedDataOutput(DataOutput out, Checksum cksum) {
    this.cksum = cksum;
    this.out = out;
}
Observa que este constructor no llama a super(out) como lo hacia el constructor de CheckedOutputStream. Esto es así porque CheckedDataOutput desciende de la clase Object en vez de una clase stream.

Estas han sido las únicas modificaciones hechas en CheckedOutputStream para crear un filtro que trabaje con objetos DataOutput.

CheckedDataInput contra CheckedInputStream

CheckedDataInput requiere los mismos cambios que CheckedDataOuput:
  • CheckedDataInput no desciende de FilterInputStream pero implementa el interface DataInput en su lugar.


    Nota: Para intentar mantener el ejemplo lo más sencillo posible, la clase CheckedDataInput realmente no está declarada para implementar el interface DataInput. Esto es así porque este interface implementa demasiados métodos. Sin embargo, la clase CheckedDataInput como está implementada en el ejemplo, si que implementa varios métodos de DataInput para ilustrar cómo deberían trabajar.

  • CheckedDataInput declare una variable privada para contener un objeto DataInput.

  • El constructor de CheckedDataInput requiere un objeto DataInput en vez de un InputStream.

Además de estos cambios también se han cambiado los métodos read(). CheckedInputStream del ejemplo original implementa dos métodos read(), uno para leer un sólo byte y otro para leer un array de bytes. El interface DataInput tiene métodos que implementan la misma funcionalidad, pero tienen diferentes nombres y diferentes firma de método. Así los métodos read() de la clase CheckedDataInput tienen nuevos nombres y firmas de método:

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);
}

Los Programas Principales

Finalmente, este ejemplo tiene dos programas principales para probar los nuevos filtros: CheckedDITest, que ejecuta los filtros en ficheros de acceso secuencial (objetos DataInputStream and DataOutputStream objects), y CheckedRAFTest, que ejecuta los filtros en ficheros de acceso aleatorio (RandomAccessFiles).

Estos dos programas principales se diferencian sólo en el tipo de objeto que abre los filtros de suma. CheckedDITest crea un DataInputStream y un DataOutputStream y utiliza el filtro sumador sobre ellos. como esto:

cis = new CheckedDataInput(new DataInputStream(
                         new FileInputStream("farrago.txt")), inChecker);
cos = new CheckedDataOutput(new DataOutputStream(
                         new FileOutputStream("outagain.txt")), outChecker);
CheckedRAFTest crea crea dos RandomAccessFiles, uno para leer y otro para escribir, y utiliza el filtro sumador sobre ellos:
cis = new CheckedDataInput(new RandomAccessFile("farrago.txt", "r"), inChecker);
cos = new CheckedDataOutput(new RandomAccessFile("outagain.txt", "rw"), outChecker);

Cuando se ejecute cualquiera de estos programas debería ver la siguiente salida:

Suma del canal de Entrada: 736868089
Suma del canal de Salida: 736868089


Ozito