Mittwoch, 8. April 2009

Serialisierbarer ByteArrayOutputStream

Ein typisches Problem in einer mehrschichtigen Web-Anwendung besteht darin, eine Datei (beispielsweise ein auf dem Server generiertes PDF-Dokument) zum Client/Web-Frontend zu übertragen (oder nach einem Datei-Upload entsprechend umgekehrt). Die einfache Lösung mit Streams schlägt fehl, sobald Sender und Empfänger nicht mehr lokal in derselben Java Virtual Machine laufen, sondern entfernt in verschiedenen JVMs. Die Streams müssten nun entweder entfernt aufrufbar (Remote) oder aber serialisierbar (Serializable) sein – beides sind sie nicht. Die Serialisierbarkeit ist standardmäßig nicht vorgesehen, da Streams geräteabhängig sind.

Eine einfache Lösung für das Versenden kleinerer Binärdateien (wenige hundert KB) macht den Standard-ByteArrayOutputStream serialisierbar, was kein Problem ist, da der ByteArrayOutputStream eine reine Java-Speicher-Implementierung und somit geräteunabhängig ist. Der Sender kann den Stream wie gewohnt mit write() füllen, und der Empfänger kann den Stream mit den üblichen Methoden auswerten. In einem Servlet kann man den Stream z.B. mit baos.writeTo(response.getOutputStream()) direkt zum Client schicken.

Für die Implementierung des serialisierbaren ByteArrayOutputStreams reicht es nicht aus, die neue Unterklasse das Serializable-Interface implementieren zu lassen. Man muss zusätzlich noch die Methoden writeObject() und readObject() implementieren, die den internen Stream-Puffer explizit serialisieren:

public class SerializableByteArrayOutputStream
extends ByteArrayOutputStream
implements Serializable {

private void writeObject(ObjectOutputStream stream)
throws IOException {
stream.writeInt( count );
stream.writeObject( buf );
}

private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
count = stream.readInt();
buf = (byte[]) stream.readObject();
}
}

Der vollständige Quelltext der Klasse
SerializableByteArrayOutputStream kann hier abgerufen werden.

Wer eine umfassendere Lösung sucht oder große Dateien übertragen muss, sollte sich die RMIIO-Bibliothek anschauen.