Lo siguiente es una lista de pasos a realizar cuando escribamos nuestro propios streams filtrados tanto de entrada como de salida.
- Crear una subclase de FilterInputStream y FilterOutputStream. Normalmente los streams de entrada y salida vienen en parejas, por eso necesitamos crear las dos versi�n del stream filtrado.
- Sobreescribir los m�todos read y write.
- Sobreescribir cualquier otro m�todo que pudieramos necesitar.
- Aseguranos de que los streams de entrada y salida funcionan juntos.
Esta secci�n nos muestra c�mo implementar nuestros propios streams filtrados a trav�s de un ejemplo que implementa una pareja de streams filtrados de entrada y salida.
Tanto el stream de entrada como el de salida usan una clase checksum para calcular el checksum de los datos escritos o le�dos desde el stream.
El checksum se usa para determinar si los datos le�dos por el stream de entrada corresponden con los datos escritos por el stream de salida.
Cuatro clases y un interface componen este programa de ejemplo:
- Las subclases de los streams de entrada y salida filtrados--CheckedOutputStream y CheckedInputStream.
- El interface Checksum y la clase Adler32 calculan un checksum para los streams.
- La clase CheckedIOTest define el m�todo main para el programa.
�La clase CheckedOutputStream
La clase CheckedOutputStream es una subclase de FilterOutputStream que calcula un checksum sobre los datos que est�n siendo escritos en el stream. Cuando se crea un CheckedOutputStream, debemos usar su �nico constructor.
public CheckedOutputStream(OutputStream out, Checksum cksum) {
super(out);
this.cksum = cksum;
}
Este constructor toma un argumento OutputStream y otro Checksum.
El argumento OutputStream es el stream de salida que este CheckedOutputStream debe filtrar. El argumento Checksum es un objeto que calcula un checksum. CheckedOutputStream se autoinicializa llamando al constructor de su superclase e inicializando una variable privada , cksum, con el objeto Checksum. El CheckedOutputStream usa cksum para actualizar el checksum cada vez que se escribe un dato en el stream.
CheckedOutputStream necesita sobreescribir los m�todos write de FilterOutputStream para que cada vez que se llame al m�todo write, se actualice el checksum. FilterOutputStream define tres versiones del m�todo write.
- write(int i)
- write(byte[] b)
- write(byte[] b, int offset, int length)
CheckedOutputStream sobreescribe estos tres m�todos.
public void write(int b) throws IOException {
out.write(b);
cksum.update(b);
}
public void write(byte[] b) throws IOException {
out.write(b, 0, b.length);
cksum.update(b, 0, b.length);
}
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
cksum.update(b, off, len);
}
Las implementaciones de estos tres m�todos write son correctas: escriben los datos en el stream de salida al que este stream est� adjuntado, luego actualiza el checksum.
�La Clase CheckedInputStream
La clase CheckedInputStream.
CheckedInputStream es una subclase de FilterInputStream que calcula un checksum de los datos que est�n siendo le�dos desde el stream. Cuando se crea un CheckedInputStream, debemos usar su �nico constructor:
public CheckedInputStream(InputStream in, Checksum cksum) {
super(in);
this.cksum = cksum;
}
Este constructor es similar al de la clase CheckedOutputStream.
S�lo que CheckedOutputStream necesita sobreescribir los m�todos write de FilterOutputStream, CheckedInputStream debe sobreescribir los m�todos read de FilterInputStream para que cada que vez que se llame al m�todo read, se actualice el checksum. Como con FilterOutputStream, FilterInputStream define tres versiones del m�todo read y CheckedInputStream las sobreescribe todas.
public int read() throws IOException {
int b = in.read();
if (b != -1) {
cksum.update(b);
}
return b;
}
public int read(byte[] b) throws IOException {
int len;
len = in.read(b, 0, b.length);
if (len != -1) {
cksum.update(b, 0, len);
}
return len;
}
public int read(byte[] b, int off, int len) throws IOException {
len = in.read(b, off, len);
if (len != -1) {
cksum.update(b, off, len);
}
return len;
}
Las implementaciones de estos tres m�todos read son correctas: leen los datos desde el stream de entrada al que est� adjunto este stream filtrado; entonces si se ley� realmente alg�n dato, se actualiza el checksum.
�El Interface Checksum y la clase Adler32
El interface Checksum define cuatro m�todos para que lo implementen los objetos checksum; estos m�todos resetean, actualizan y devuelven el valor del checksum. Podr�amos escribir una clase Checksum que calcule un tipo espec�fico de checksum como el CRC-32.
Observa que la herencia en el checksum es la noci�n de estado. El objeto checksum no s�lo calcula un checksum y ya est�. En vez de eso, el checksum es actualizado cada vez que se lee o se escribe informaci�n en el stream para el que este objeto calcula el checksum. Si queremos reutilizar un objeto checksum, debemos resetearlo.
Para este ejemplo, hemos implementado el checksum Adler32, que es casi una versi�n del checksum CRC-32 pero puede ser calculado m�s r�pidamente.
�Un Programa de Prueba
La �ltima clase del ejemplo, CheckedIOTest, contiene el m�todo main para el programa.
import java.io.*;
public class CheckedIOTest {
public static void main(String[] args) throws IOException {
Adler32 inChecker = new Adler32();
Adler32 outChecker = new Adler32();
CheckedInputStream in = null;
CheckedOutputStream out = null;
try {
in = new CheckedInputStream(
new FileInputStream("farrago.txt"),
inChecker);
out = new CheckedOutputStream(
new FileOutputStream("outagain.txt"),
outChecker);
} catch (FileNotFoundException e) {
System.err.println("CheckedIOTest: " + e);
System.exit(-1);
} catch (IOException e) {
System.err.println("CheckedIOTest: " + e);
System.exit(-1);
}
int c;
while ((c = in.read()) != -1)
out.write(c);
System.out.println("Input stream check sum: " +
inChecker.getValue());
System.out.println("Output stream check sum: " +
outChecker.getValue());
in.close();
out.close();
}
}
El m�todo main crea dos objetos Adler32, uno para un CheckedOutputStream y otro para un CheckedInputStream. El ejemplo requiere dos objetos checksum porque los objetos checksum se actualizan durante las llamadas a lo m�todos read y write que ocurren concurrentemente.
Luego, main abre un CheckedInputStream sobre un peque�o fichero de texto, farrago.txt, y un CheckedOutputStream sobre un fichero de salida llamado outagain.txt, que no existe hasta que ejecutemos el programa por primera vez.
El m�todo main lee el texto desde el CheckedInputStream y simplemente lo copia en el CheckedOutputStream. Los m�todos read y write usan los objetos Adler32 para calcular un checksum durante la lectura y escritura. Despu�s de haber le�do completamente el fichero de entrada (y consecuentemente se haya completado de escribir el fichero de salida), el programa imprime los checksums de los dos streams (que deben ser iguales) y cierra los dos streams.
Cuando ejecutemos CheckedIOTest, deber�amos ver esta salida:
Input stream check sum: 736868089 Output stream check sum: 736868089
�Filtrar Ficheros de Acceso Aleatorio
Todos los streams filtrados de java.io descienden de InputStream o OutputStream, que implementan ficheros de acceso secuencial. Por eso si subclasificamos FilterInputStream o FilterOutputStream nuestros streams filtrados tambi�n ser�n ficheros de acceso secuencial.
Escribir Filtros para Ficheros de Acceso Aleatorio, m�s adelante en esta lecci�n nos muestra c�mo re-escribir este ejemplo para que funcione sobre un RandomAccessFile tambi�n como sobre un DataInputStream o un DataOutputStream.