12.2 Persistente Objekte
Transcrição
12.2 Persistente Objekte
12.2 Persistente Objekte Persistenz im engeren Sinn bedeutet: Langzeitspeicherung für beliebig getypte Objekte Ideal Orthogonale Persistenz : Lebensdauer und Typ von Objekten sind unabhängige Konzepte alp3-12.2 1 12.2.1 Persistente Programmiersprachen Ideal: Folgendes sollte möglich sein: Table t = new Table(); t.enter(key,data); ... t.put("myTable4711"); ... und am nächsten Tag: // Effekt: // Objekt „ist auch Datei“ Table t; try { t = Table.get("myTable4711"); } catch(FileNotFoundException e) {...} catch(ClassCastException e) {...} mit put und get in der Klasse Object ... alp3-12.2 2 Anforderungen und Probleme: Objektidentität muss gewahrt bleiben. Kopieren des Objektwertes von der Halde in eine Datei und zurück gewährleistet das nicht. Insbesondere wenn mehrere Benutzer/Prozesse ein persistentes Objekt benutzen, sollten sie nicht etwa unabhängige Kopien benutzen. (Beachte: bei normalen Dateien ist das gesichert!) Konsequenz: keine getrennte Speicherverwaltung für virtuellen Speicher und Dateispeicher (Objekt = Segment = Datei) persistente Betriebssysteme persistente Programmiersprache (z.B. Napier88) alp3-12.2 3 Annäherung an das Ideal: Objekt in Datei schreiben bzw. aus Datei lesen, erfordert Serialisierung eines Objektgeflechts beim Kopieren von der Halde in die Datei bzw. Deserialisierung beim Wiederaufbau des Geflechts auf der Halde, wird von Java unterstützt. alp3-12.2 4 12.2.2 Objektserialisierung in Java http://java.sun.com/javase/6/docs/platform/serialization/spec/serialTOC.html In Java können nicht nur die Werte primitiver Datentypen, sondern auch die Werte von Objekten ausgegeben werden: interface ObjectOutput extends DataOutput* { ..... void writeObject(Object x) throws IOException; } interface ObjectInput extends DataInput* { ..... Object readObject() throws IOException, ClassNotFoundException; } * für primitive Datentypen (12.1.3.2) alp3-12.2 5 Serialisierung in einen Ausgabestrom: class ObjectOutputStream extends OutputStream* implements ObjectOutput ...{ ObjectOutputStream(OutputStream out) {...} ..... void writeObject(Object x) throws IOException {...} } Das Objekt x wird in binärer Form - in geeignet serialisierter Darstellung - in den Strom out geschrieben. Wenn das Objekt Verweise enthält, wird der gesamte am Objekt hängende Objekt-Graph in geeignet serialisierter Darstellung in den Strom geschrieben. * Byte-Strom (12.1.3.1) alp3-12.2 6 Deserialisierung aus einem Eingabestrom: class ObjectInputStream extends InputStream* implements ObjectInput ...{ ObjectInputStream(InputStream out) {...} ..... Object readObject() throws IOException, ClassNotFoundException {..} } Der Inhalt eines serialisierten Objekts wird eingelesen und als neu erzeugtes Objekt abgeliefert. Die beteiligten Klassen müssen geladen bzw. ladbar sein. Dabei wird der gesamte Objektgraph des ursprünglichen Objekts wieder aufgebaut. * Byte-Strom (12.1.3.1) alp3-12.2 7 Ein einfaches Beispiel: // store current date in a file: ObjectOutput out = new ObjectOutputStream( new FileOutputStream("today")); out.writeObject("This text was written on "); out.writeObject(new Date()); out.flush(); // retrieve file contents: ..... ObjectInput in = new ObjectInputStream( new FileInputStream("today")); String text = (String)in.readObject(); Date date = (Date) in.readObject(); $ od -c today 0000000 254 355 0000020 w 0000040 s r 0000060 t e 0000100 \b \0 alp3-12.2 \0 005 t \0 031 T h i s a s w r i t t e \0 016 j a v a . u t h j 201 001 K Y t 031 003 \0 001 & 223 m 254 021 x t n i \0 l \0 e o . x x n D p 8 t a w ... und das Kleingedruckte: Ein zu serialisierendes Objekt muss die Schnittstelle interface Serializable { } implementieren. Das gilt für alle Objekte im Objektgraph. Statische Attribute bleiben beim Serialisieren unberücksichtigt (weil es Klassendaten, nicht Objektdaten sind). Attribute, die mit dem Modifizierer transient markiert sind, bleiben beim Serialisieren unberücksichtigt; sie werden beim späteren Einlesen des Objekts mit den typspezifischen Voreinstellungen initialisiert. alp3-12.2 9 ... und zum Thema Typsicherheit: Bla bla = (Bla)in.readObject(); ClassCastException: die Klasse des eingelesenen Objekts ist nicht die erwartete ClassNotFoundException: die Klasse des eingelesenen Objekts ist nicht greifbar Externe Darstellung des Objekts enthält Informationen, die seine Klasse identifizieren - genauer: Klassenname, Schnittstellen Attribute Signaturen der nichtprivaten Methoden ... weitere Hashwert über diese Informationen Daher: andere Version der Klasse akzeptabel, sofern die Änderungen sich in gewissen Grenzen halten. alp3-12.2 10 Beispiel: privates Telefonbuch als Datei phbook - zeilenorientierte Benutzung: $ java PhoneBook phbook enter command : Lorenz 8255198 : d Lorenz deleted : a Lorenz 123456 added : q bye $ alp3-12.2 11 public class PhoneBook { public static void main(String[] arg) throws IOException, ClassNotFoundException { Map<String,Long> book; File file = new File(arg[0]); if(file.createNewFile()) // create new phbook book = new HashMap<String,Long>(); else { // load data from file InputStream s = new FileInputStream(file); ObjectInput in = new ObjectInputStream(s); book = (Map<String,Long>)in.readObject(); in.close(); } // interact with user: alp3-12.2 12 System.out.println("enter command"); // user interaction BufferedReader user = new BufferedReader( new InputStreamReader(System.in)); loop: for(;;) { System.out.print(": "); // prompt String line = user.readLine(); if(line.equals("")) continue; String[] cmd = line.split(" "); if(cmd[0].length()==1) // command proper switch(line.charAt(0)) { case 'a': book.put(cmd[1], Long.parseLong(cmd[2])); break; case 'd': book.remove(cmd[1]); break; case 'q': break loop; // quit default: System.out.println("?"); } else // lookup System.out.println(book.get(cmd[0])); }//loop alp3-12.2 13 ObjectOutput out = new ObjectOutputStream( new FileOutputStream(file)); out.writeObject(book); out.close(); System.out.println("bye"); } // end of main method } // end of class PhoneBook Resümee: Eingabe eines ausgegebenen Objekts erzeugt Kopie des alten Objekts. Wenn zwei Prozesse ein Objekt einlesen, erhalten sie unabhängige Kopien. Objektidentität geht verloren. Gemeinsame Teilobjekte werden dupliziert. alp3-12.2 14 Hinweis: Externalisierung ist Alternative zur Serialisierung: Zu externalisierendes Objekt stellt selbst Methoden zum Abspeichern und Wiederherstellen bereit! interface Externalizable implements Serializable { void writeExternal(ObjectOutput out) throws ...; void readExternal (ObjectInput in) throws ...; } http://java.sun.com/javase/6/docs/platform/serialization/spec/serialTOC.html alp3-12.2 15 12.2.3 Memory-Mapped Files (12.1.3.1 - byteorientierter Dateizugriff:) class FileOutputStream extends OutputStream { FileOutputStream(String pathname) {...} ..... void write(int by) {...} FileChannel getChannel() {...} } Liefert den dem FileOutputStream zugrundeliegenden Kanal vom Typ FileChannel- d.i. der zum Lesen bzw. Schreiben verwendete Iterator, den das Betriebssystem beim Öffnen einer Datei bereitstellt (z.B. in Unix identifizierbar über den „file descriptor“). ( auch für FileInputStream und RandomAccessFile ) alp3-12.2 16 java.nio.channels („new I/O“) : abstract abstract abstract abstract // abstract // abstract class FileChannel ... { int read (ByteBuffer buf); int write(ByteBuffer buf); long position(); get read/write pointer FileChannel position(long pos); set read/write pointer - „seek“ long transferFrom(ReadableByteChannel src, long pos, long count); abstract long transferTo(long pos, long count, WritableByteChannel dst); abstract MappedByteBuffer map(FileChannel.MapMode mode, long pos, long size); ..... } alp3-12.2 17 abstract MappedByteBuffer map(FileChannel.MapMode mode, long pos, long size) ... Es wird ein MappedByteBuffer-Pufferobjekt erzeugt, das als Fenster auf einen Bereich der Datei fungiert („memory-mapped file“ in Betriebssystem-Terminologie). Der FileChannel muss zum Lesen und/oder Schreiben geöffnet sein, wenn im Pufferobjekt gelesen bzw. geschrieben werden soll. mode: READ_ONLY READ_WRITE PRIVATE alp3-12.2 Ausnahmemeldung bei Schreibversuch Änderungen werden irgendwann in der Datei sichtbar und können auch in anderen Pufferobjekten sichtbar werden (sic!). private Kopie 18 class MappedByteBuffer extends ByteBuffer ... abstract class ByteBuffer extends Buffer ... { byte[] array() {...} // the byte array inside the buffer abstract CharBuffer asCharBuffer(); // ... viewed as characters abstract IntBuffer asIntBuffer(); // ... viewed as integers ..... abstract double getDouble(); abstract float getFloat(); ..... abstract ByteBuffer putDouble(double x); abstract ByteBuffer putFloat(float x); ..... } alp3-12.2 19 Persistentes Objekt vom Typ byte[]: File f = new File("bytes"); long size = f.length(); FileChannel ch = new RandomAccessFile(f,"rws") .getChannel(); MappedByteBuffer buf = map(FileChannel.MapMode.READ_WRITE, 0, size); byte[] bytes = buf.array(); ..... // operate on byte array object Entsprechendes geht auch für getypte Daten. Aber: nicht für Objekte beliebiger Klassen. alp3-12.2 20 12.2.4 Objektorientierte Datenbanken ... kombinieren die Möglichkeiten von Datenbanken mit dem Prinzip der Objekt-Persistenz - aber das sprengt den Rahmen von ALP III ! [ Dies ist die letzte Folie von ALP III im WS 09/10 ] alp3-12.2 21