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