2. Java Properties

Werbung
Introspektive Konfiguration eines
Portalsystems für das Wissensmanagement
Ausarbeitung zum Systementwicklungsprojekt
WS 2003/04
Katharina Brendebach
Katy Kirsche
Betreuer: Dipl. Ing. Thomas Büchner
Lehrstuhl für Software Engineering betrieblicher Informationssysteme
Prof. Dr. Florian Matthes
Technische Universität München
Inhaltsverzeichnis
Inhaltsverzeichnis _______________________________________________________________ i
Abbildungsverzeichnis __________________________________________________________ ii
1.
2.
Einleitung ________________________________________________________________ 1
Java Properties ____________________________________________________________ 2
2.1.
2.2.
2.3.
2.4.
3.
Konzept _____________________________________________________________________ 4
Funktionsweise _______________________________________________________________ 6
Vorteile ____________________________________________________________________ 12
Nachteile ___________________________________________________________________ 13
Zusammenfassung ____________________________________________________________ 13
Introspektion_____________________________________________________________ 14
4.1.
4.2.
4.3.
4.4.
4.5.
5.
2
2
3
4
Java Preferences ___________________________________________________________ 4
3.1.
3.2.
3.3.
3.4.
3.5.
4.
Funktionsweise _______________________________________________________________
Vorteile _____________________________________________________________________
Nachteile ____________________________________________________________________
Zusammenfassung _____________________________________________________________
Konzept ____________________________________________________________________
Funktionsweise ______________________________________________________________
Vorteile ____________________________________________________________________
Nachteile ___________________________________________________________________
Zusammenfassung ____________________________________________________________
14
14
18
19
19
Resümee_________________________________________________________________ 19
Literaturverzeichnis ___________________________________________________________ 20
i
Abbildungsverzeichnis
Abbildung 1: Namensräume in Properties- Datei _________________________________________________ 2
Abbildung 2 UML- Diagramm der Preferences API (Teilausschnitt) __________________________________ 5
Abbildung 3 Java- Klasse mit Preferences ______________________________________________________ 7
Abbildung 4 Knotenhierarchie in der Windows- Registry___________________________________________ 7
Abbildung 5 Codebeispiel für node()- Methode __________________________________________________ 8
Abbildung 6 Resultat der node()- Methode in der Windows- Registry _________________________________ 8
Abbildung 7 Exportierung von Preferences in XML- Dateien _______________________________________ 9
Abbildung 8 SystemNode.xml _______________________________________________________________ 10
Abbildung 9 UserTree.xml _________________________________________________________________ 11
Abbildung 10 Import aus XML- Datei in Preference- Baum________________________________________ 11
Abbildung 11 Ausschnitt aus der GUI des Online Brokers des SEBIS- Lehrstuhls ______________________ 15
Abbildung 12 Dieser Code erzeugt einen Attribut- Knoten in der GUI _______________________________ 16
Abbildung 13 Interfaces und Klassen des Connect- Packages (Teilausschnitt) _________________________ 17
1. Einleitung
Bei der Benutzung von Software ist es oftmals nötig, dass der Benutzer oder der
Systemadministrator benutzerspezifische Einstellungen machen möchte, um die Software an
die persönlichen Vorlieben anzupassen oder um die Software auf die zu erfüllenden
Anforderungen besser einzustellen. Das Festlegen dieser Voreinstellungen nennt man
Konfiguration.
Beim Konfigurieren der Software, d.h. der Voreinstellung oder Veränderung der
Bedienungsparameter, werden Konfigurationsdaten erzeugt. Diese Konfigurationsdaten
müssen verwaltet werden, damit sie abgerufen und bearbeitet werden können. Bei den
Konfigurationsdaten unterscheidet man zwischen Benutzerdaten (User data) und Systemdaten
(System data). Je nachdem um welche Art von Konfigurationsdaten es sich handelt, ergeben
sich unterschiedliche Ansprüche an die Verwaltungskonzepte.
Während der Softwarebenutzung werden neben den Konfigurationsdaten auch so genannte
Applikationsdaten erzeugt (wie z.B. Schriftdokumente, Datenbankeinträge). Die Verwaltung
dieser Daten soll aber nicht das Thema dieser Arbeit sein.
Im Folgenden werden die zwei Arten von Konfigurationsdaten genauer spezifiziert:
User Data
• Benutzerdaten sind aktive Daten, die ständig erzeugt und geändert werden.
• Diese Daten werden dynamisch zur Laufzeit generiert.
• Das Handling der Daten ist unwichtig, weil das System die Daten selbstständig verwaltet.
Der Administrator muss keine Konfigurationen vornehmen, d.h. die Konfigurationsdaten
müssen nicht eingesehen werden.
• Die Daten sind nicht essentiell für die Funktionalität der Applikation.
• Es bedarf keiner Kenntnis über Art und Speicherort der Attribute.
• Es ist keine Dokumentation notwendig.
Beispiel: Der Benutzer muss Möglichkeiten haben für sich spezifische Einstellungen
vornehmen zu können, wie z.B. in der Entwicklungsumgebung Eclipse durch die Wahl der
JDK- Version. In Eclipse ist dies über den Menüpunk Preferences möglich.
System Data
• Systemdaten sind statische Daten.
• Diese Daten werden einmalig vor dem Systemstart gesetzt.
• Das Handling ist sehr wichtig, weil die Daten editiert und konfiguriert werden müssen und
dabei eingesehen werden müssen.
• Die Daten sind systemkritisch und essentiell für die Funktionalität der Applikation.
• Es ist wichtig zu wissen, wo ein Attribut gesetzt und wo es gespeichert wird.
• Die Dokumentation der Einträge ist sehr wichtig.
Beispiel: Die Entwicklungsumgebung Eclipse, die in Java programmiert wurde, benötigt zum
Systemstart bestimmte Konfigurationsdaten.
1
Im Folgenden werden drei Ansätze vorgestellt, wie man Konfigurationsdaten verwaltet:
• Der althergebrachte java.util.Properties- Ansatz
• Der neue komfortablere java.util.prefs.*- Ansatz
• Der innovative Ansatz der Introspektion mit GUI
Wie sich jeweils die vorgestellten Konzepte für die Verwaltung von User und System data
eignen, wird am Ende jedes Kapitels resümiert.
2. Java Properties
2.1.
Funktionsweise
Dieses Konzept zur Konfigurationsdatenverwaltung wurde von Sun entwickelt und ist seit
dem JDK 1.0 verfügbar.
Die Properties werden in Properties- Objekte geschrieben, die wiederum in Textdateien
gespeichert werden.
Um Properties zu bündeln, wie z.B. die Properties eines Packages, kann eine Hierarchie von
Namensräumen eingeführt werden.
myapp.payment.SSLPort=10256
myapp.payment.SETPort=10257
myapp.admin.listenPort=10258
Abbildung 1: Namensräume in Properties- Datei
Die Properties eines Softwaresystems können allesamt in eine Datei geschrieben werden, oder
auch in verschiedene Dateien, um z.B. die Properties für bestimmte Teile einer Anwendung
voneinander zu trennen.
2.2.
Vorteile
Das Properties- Konzept stellt einen sehr simplen Ansatz für die Verwaltung von
Konfigurationsdaten dar.
Durch die Speicherung der Properties in Dateien ist ein Plattformwechsel einfach möglich. Es
muss nur die Properties- Datei kopiert werden, damit alle Konfigurationen erhalten bleiben.
Es ist ebenfalls leicht ein Vergleich zwischen verschiedenen Versionen von Konfigurationen
über diverse Diff- Tools wie z.B. ExamDiff Pro möglich.
2
Zudem ist es dem Programmierer möglich, die Properties ausführlich in der Properties- Datei
zu dokumentieren.
2.3.
Nachteile
Das Properties- Konzept erschwert die Skalierbarkeit. Je mehr Properties- Dateien angelegt
werden, desto schwieriger wird es für den Programmierer den Überblick zu behalten.
Problematisch sind auch die im Code fest verdrahteten Properties- Dateinamen.
Zudem kann es bei der Namensgebung der Properties zu Namenskollisionen kommen, wenn
zwei Properties in einer Datei gleich benannt werden.
Sehr schwierig gestaltet sich auch die Konsistenzhaltung der Konfigurationsdaten. Der
Programmierer muss die Properties in den verschiedenen Dateien aktiv überwachen. Es gibt
keinen Mechanismus, der ihm dabei hilft. Zudem gibt keine Verknüpfung der PropertiesDateien mit dem Code. Somit gibt es auch keine Möglichkeit, die Properties auf
Vollständigkeit zu überprüfen.
Die Properties API unterstützt die Hierarchie von Namensräumen nicht, d.h. es werden keine
Funktionen bereitgestellt, die es ermöglichen, auf den Propertiesnamen zu navigieren. Der
Programmier muss deshalb eigenen Code schreiben, um die (Sub)Properties auslesen und
verarbeiten zu können.
Das Properties- Konzept sieht nur String- Typen vor. Alle anderen Typen müssen umständlich
in String- Typen umgewandelt werden, bevor sie in den Properties gespeichert werden
können. Beim Auslesen ist ebenfalls wieder eine Typkonvertierung der String- Typen in die
jeweils geforderten Typen notwendig. Auch hier stellt die Properties API dem Programmierer
keine Funktionen zur Verfügung, um ihm die Programmierarbeit für die Typenkonvertierung
und das Auslesen abzunehmen.
Es ist keine Angabe von Default- Werten möglich. Ist dies dennoch gewünscht, müssen diese
Werte umständlich innerhalb des Codes, z.B. durch Null- Abfragen gesetzt werden.
Der Programmierer muss die Daten explizit speichern und laden, d.h. die Konfigurationsdaten
mit einem Stream in die Properties- Datei schreiben. Die Properties API stellt keine
Funktionen zur Verfügung, die das Ein- und Auslesen der Properties in die Properties Dateien
übernehmen.
Für die Anwendung des Properties- Konzeptes gibt es keinerlei Konventionen. Der
Speicherort der Properties- Datei ist z.B. nicht spezifiziert, im Gegensatz z.B. zum WEB-INF
Ordner beim Tomcat, in dem die web.xml- Datei für die Anwendung gespeichert werden
muß. Dies erschwert den gemeinsamen Zugriff von verschiedenen Applikationen auf
dieselben Konfigurationsdaten, da zunächst geklärt werden muss, wo die entsprechende
Properties- Datei zu finden. Zudem kann es zu Problemen mit den Leserechten an den
Properties- Dateien kommen, wenn sie in Verzeichnissen abgespeichert werden, auf die der
Programmierer nicht zugreifen darf.
Und zu guter Letzt gibt es keinerlei Namenskonventionen für die Properties- Dateien.
3
2.4.
Zusammenfassung
Grundsätzlich ist es möglich mit dem Properties- Konzept Benutzer- und Systemdaten zu
speichern und zu konfigurieren. Für beide Arten von Konfigurationsdaten stellt dieses
Konzept allerdings keine optimale Lösung dar. Die oben angeführten Nachteile sprechen für
sich. Sowohl für die Benutzer- wie auch die Systemdaten stellt das Properties- Konzept eine
sehr unkomfortable Art der Verwaltung dar. Die Daten müssen umständlich in die PropertiesDateien geschrieben und wieder aus ihnen geladen werden. Es wird keinerlei Hilfe bei der
Verwaltung der Properties durch die API gegeben.
3. Java Preferences
3.1.
Konzept
Java Preferences sind seit dem J2SE 1.4 verfügbar.
Preferences stellen ein Konzept zur Verwaltung von system- oder benutzerspezifischen
Konfigurationsdaten dar.
Das Preferences Konzept basiert auf einem einfachen Ansatz: Das Package java.util.prefs.*
enthält lediglich 3 Schnittstellen, 4 Klassen und 2 Ausnahmen.
4
java.util.prefs.Preferences
+addNodeChangeListener(NodeChangeListener ncl) : void
+addPreferenceChangeListener(PreferenceChangeListener pcl) : void
+childrenNames() : String []
+exportNode(OutputStream os) : void
+exportSubtree(OutputStream os) : void
+get(String key, String def) : String
+getBoolean(String key, boolean def) : boolean
+getDouble(String key, double def) : double
+getInt(String key, int def) : int
+put(String key, String value) : void
+putBoolean(String key, boolean value) : void
+putDouble(String key, double value) : void
+putInt(String key, int value) : void
+removeNode() : void
+node(String path) : Preferences
+toString() : String
+static importPreferences(InputStream is) : void
+static systemNodeForPackage(Class c) : Preferences
java.util.prefs.PreferencesFactory
<<interface>>
java.util.EventListener
<<interface>>
+systemRoot() : Preferences
+userRoot() : Preferences
java.util.prefs.NodeChangeListener
<<interface>>
+childAdded(NodeChangeEvent evt) : void
+childRemoved(NodeChangeEvent evt) : void
+static userNodeForPackage(Class c) : Preferences
+static systemRoot() : Preferences
+static userRoot() : Preferences
java.util.prefs.AbstractPreferences
+addNodeChangeListener(NodeChangeListener ncl) : void
+addPreferenceChangeListener(PreferenceChangeListener pcl) : void
+exportNode(OutputStream os) : void
+exportSubtree(OutputStream os) : void
#getChild(String nodeName) : AbstractPreferences
+get(String key, String def) : String
+getBoolean(String key, boolean def) : boolean
+getDouble(String key, double def) : double
+getInt(String key, int def) : int
+put(String key, String value) : void
+putBoolean(String key, boolean value) : void
+putDouble(String key, double value) : void
+putInt(String key, int value) : void
+removeNode() : void
+node(String path) : Preferences
java.util.prefs.PreferenceChangeListener
<<interface>>
+preferencesChange(PreferenceChangeEvent evt) : void
java.util.EventObject
java.util.prefs.NodeChangeEvent
java.util.prefs.PreferenceChangeEvent
+getChild() : Preferences
+getParent() : Preferences
+getKey() : String
+getNewValue() : String
+getNode() : Preferences
java.lang.Exception
java.util.prefs.BackingStoreException
Abbildung 2 UML- Diagramm der Preferences API (Teilausschnitt)
5
java.util.prefs.InvalidPreferencesFormatException
Die Preferences API ist back- end neutral, d.h. der Programmierer kümmert sich nicht darum,
ob die Daten in Dateien, Datenbanken oder plattformspezifischen Speicher (z.B. der Windows
Registry) gespeichert werden.
Wichtig für die Implementierung des Preferences- Konzept ist die Preferences- Klasse, die
alle wesentlichen Funktionalitäten bereitstellt.
Die Grundlage für die Preferences API ist eine hierarchische Struktur. Die Verwaltung der
Konfigurationsdaten wird durch baumähnliche Sammlungen von Knoten realisiert, ähnlich
der Verzeichnisse in einem hierarchischen Dateisystem.
Ein Knoten kann mehrere Attribute enthalten, die durch Name- Wert- Paare repräsentiert
werden. Man unterscheidet Knoten, die Verzeichnisse darstellen und Attribute und weitere
Verzeichnisse enthalten, und Knoten, die Blätter auf der untersten Hierarchieebene darstellen
und nur Attribute enthalten.
Der Preferences Baum kann wie ein Verzeichnisbaum durchlaufen werden. Es gib einen
Wurzelknoten, der durch ‘‘/‘‘ angesprochen werden kann. Die verschiedenen
Hierarchieebenen können durch absolute und relative Pfadabgaben erreicht werden.
Es gibt zwei 2 Arten von Preference- Bäumen:
Der System Preference- Baum, der Konfigurationsdaten für alle Benutzer des Systems
verwaltet, wie z.B. die Installationskonfigurationsdaten für eine Anwendung.
Der User Preference- Baum, der die Konfigurationsdaten für jeweils einen Benutzer
verwaltet, z.B. die Schriftart, die Farbwahl, die Fensteranordnung und –größe für eine
bestimmte Anwendung.
3.2.
Funktionsweise
Die Implementierung der Preferences API wird wie folgt realisiert: Zunächst wird ein
Preferences Objekt durch eine von 4 statischen Factory- Methoden erzeugt. Der Konstruktor
der Preferences- Klasse ist nicht aufrufbar.
public static Preferences systemNodeForPackage(Object o)
public static Preferences userNodeForPackage(Object o)
Diese beiden Methoden liefern ein Preferences Objekt bzw. Knoten zurück, dessen Pfad den
Package- Namen des zu übergebenden Objektes repräsentiert. Der Knoten muss erzeugt
werden, wenn er vor dem Methodenaufruf noch nicht existiert. Das zu übergebende Objekt ist
die Instanz einer Klasse, in der die Konfigurationen gesetzt und gespeichert werden sollen.
Das Package, zu der die Klasse gehört, korrespondiert also mit dem Pfad des PreferencesKnoten. Diese Verbindung bleibt auch erhalten, wenn der Package- Name verändert wird. Es
gibt zwei Methoden, mit denen ein Preferences- Knoten erzeugt werden kann, je nachdem ob
ein Knoten im System Preference- Baum oder im User Preference- Baum zurückgegeben
werden soll.
6
package com.acme.myapp.user;
import java.util.prefs.Preferences;
public class Payer {
private static final String PREFERRED_CHANNEL = "PreferredChannel";
private PreferredChannel preferredChannel = PreferredChannel.WML;
public void storePreferences() {
Preferences prefs = Preferences.userNodeForPackage(this);
prefs.put(PREFERRED_CHANNEL, preferredChannel.toString());
// The rest of the code here...
}
public static void main(String[] args) {
// The test code new Payer().storePreferences();
}
}
Abbildung 3 Java- Klasse mit Preferences
Abbildung 4 Knotenhierarchie in der Windows- Registry
public static Preferences systemRoot()
public static Preferences userRoot()
Diese beiden Methoden geben die Wurzel des System- bzw. User- Preference- Baumes
zurück, ohne dass es eines Übergabeparameters bedarf.
Es können neben einzelnen Knoten auch ganze Äste erzeugt und natürlich auch durchlaufen
werden.
public abstract Preferences node(String pathName)
Diese Methode ermöglicht das Traversieren der Preference- Bäume. Als Übergabeparameter
können absolute oder relative Pfade angegeben werden.
7
Preferences prefs = Preferences.systemRoot().node("/the/first/node");
Preferences prefs2 = prefs.node("the/second/node");
Preferences prefs3 = prefs.node("/yet/another/branch");
Abbildung 5 Codebeispiel für node()- Methode
Abbildung 6 Resultat der node()- Methode in der Windows- Registry
Um Konfigurationsdaten schließlich in den erzeugten Preference- Bäumen setzen und
abfragen zu können, gibt es put- und get- Methoden. Für alle primitiven Datentypen gibt es
eigene Implementierungen dieser Methoden, die jeweils zwei Übergabeparametern
(Knotenname und Knotenwert) benötigen.
Mit Hilfe des Beobachter- Musters können die Preferences auch überwacht werden. Dazu
wird ein NodeChangeListener bei einem Knoten registriert, der Auskunft gibt, ob ein Kind
unterhalb des Knotens hinzugefügt oder entfernt wurde.
Der Import/ Export von Preferences, z.B. um einen Plattformwechsel zu bewerkstelligen, ist
ebenfalls möglich. Dazu werden einzelne Knoten oder ganze (Sub)Bäume in XML exportiert.
Aus diesen XML- Dateien können die Preferences wieder importiert werden.
In Abbildung 7 ist im Code der Klasse PreferencesExporter nachzulesen, wie die
Preferences in XML- Dateien exportiert werden.
8
package org.acme.testexim;
import java.util.prefs.*;
import java.io.*;
public class PreferencesExporter {
private static final String PACKAGE = "/org/acme/testexim";
public static void main(String[] args) {
doThings(Preferences.systemRoot().node(PACKAGE));
doThings(Preferences.userRoot().node(PACKAGE));
}
public static void doThings(Preferences prefs) {
prefs.putBoolean("Key0", false);
prefs.put("Key1", "Value1");
prefs.putInt("Key2", 2);
Preferences grandparentPrefs = prefs.parent().parent();
grandparentPrefs.putDouble("ParentKey0", Math.E);
grandparentPrefs.putFloat("ParentKey1", (float)Math.PI);
grandparentPrefs.putLong("ParentKey2", Long.MAX_VALUE);
String fileNamePrefix = "System";
if(prefs.isUserNode()) {
fileNamePrefix = "User";
}
try {
OutputStream osTree =
new BufferedOutputStream(
new FileOutputStream(fileNamePrefix + "Tree.xml"));
grandparentPrefs.exportSubtree(osTree);
osTree.close();
OutputStream osNode =
new BufferedOutputStream(
new FileOutputStream(fileNamePrefix + "Node.xml"));
grandparentPrefs.exportNode(osNode);
osNode.close();
} catch(IOException ioEx) {
// ignore
} catch(BackingStoreException bsEx) {
// ignore too
}
}
}
Abbildung 7 Exportierung von Preferences in XML- Dateien
9
Die Klasse PreferencesExporter erzeugt 4 XML- Dateien:




SystemNode.xml
SystemTree.xml
UserNode.xml
UserTree.xml
Der Inhalt von zwei der vier erzeugten Dateien wird in Abbildung 8 und Abbildung 9
dargestellt. Der Unterschied zwischen der Speicherung eines einzelnen Knotens und eines
ganzen (Sub)Baumes wird dabei ersichtlich.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>
<preferences EXTERNAL_XML_VERSION="1.0">
<root type="system">
<map />
<node name="org">
<map>
<entry key="ParentKey0" value="2.718281828459045" />
<entry key="ParentKey1" value="3.1415927" />
<entry key="ParentKey2" value="9223372036854775807" />
</map>
</node>
</root>
</preferences>
Abbildung 8 SystemNode.xml
10
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>
<preferences EXTERNAL_XML_VERSION="1.0">
<root type="user">
<map />
<node name="org">
<map>
<entry key="ParentKey0" value="2.718281828459045" />
<entry key="ParentKey1" value="3.1415927" />
<entry key="ParentKey2" value="9223372036854775807" />
</map>
<node name="acme">
<map />
<node name="testexim">
<map>
<entry key="Key0" value="false" />
<entry key="Key1" value="Value1" />
<entry key="Key2" value="2" />
</map>
</node>
</node>
</node>
</root>
</preferences>
Abbildung 9 UserTree.xml
Um die XML- Dateien wieder in einen Preference- Baum importieren zu können, bedarf es
nur der wenigen Zeilen Code in Abbildung 10.
InputStream is = new BufferedInputStream(
new FileInputStream(args[0]));
Preferences.importPreferences(is);
is.close();
Abbildung 10 Import aus XML- Datei in Preference- Baum
Das Speichermedium, in dem die Konfigurationsdaten schlussendlich gespeichert werden, ist
von der Implementierung abhängig. In J2SE 1.4 Beta wird die Implementierung spezifiziert,
indem die Systemvariable java.util.prefs.PreferencesFactory auf den Namen der
Implementierungsklasse gesetzt wird.
11
Angenommen man hätte eine eigene Implementierung der Preferences Factory geschrieben,
die z.B. PostGreSQL als Hintergrundspeicher benutzt, und demzufolge mit
PostGreSQLPreferencesFactory betitelt ist.
Dann würde man mit der folgenden Zuweisung die eigene Preferences Factory setzen:
-Djava.util.prefs.PreferencesFactory= PostGreSQLPreferencesFactory
Sun sieht standardmäßig für Windows die Windows Registry als Hintergrundspeicher vor.
Um eine Preferences-Instanz zu erzeugen, wird eine Factory verwendet, die als
Umgebungsvariable von Java festgelegt ist. Unter Windows ist das standardmäßig die Factory
java.util.prefs.WindowsPreferencesFactory. Diese Fabrik erzeugt Instanzen der
Klasse java.util.prefs.WindowsPreference. Als Datenspeicher wird durch diese Factory
die Windows-Registry-Datenbank festgelegt. Die System- Preferences werden unter dem
Schlüssel ”HKEY LOCAL MACHINE\Software\JavaSoft\Prefs“ abgelegt und die BenutzerPreferences unter dem Schlüssel ”HKEY CURRENT USER\Software\JavaSoft\Prefs“ .
3.3.
Vorteile
Die Organisation der Konfigurationsdaten ist durch die Verwendung der Preferences API sehr
einfach, weil die Daten mit dem Package verbunden werden, zu dem sie gehören.
Durch die Möglichkeit des Export/ Import mit XML müssen die Preferences bei einem
Plattformwechsel nicht neu gesetzt werden, ähnlich den Properties- Dateien, die einfach
kopiert werden können.
Preferences sind nicht auf Strings beschränkt, es werden alle primitiven Datentypen
unterstützt: Boolean, byte[], double, float, int, long.
Es sind unterschiedliche (eigene) Preferences- Implementierungen möglich, z.B. um
unterschiedliche Speichermedien zu integrieren. Dazu muss der Standard Java Mechanismus
geändert werden und jeweils eine andere Factory eingestellt werden.
Da die get- Methoden mit Defaultwerten arbeiten (siehe nachfolgend die Nachteile)
funktioniert die Preferences API auch, wenn der Zusatzspeicher ausfällt.
Der Programmierer muss sich nicht um das Speichern und Laden der Konfigurationsdaten
explizit kümmern, im Gegensatz zu dem Properties- Ansatz.
Die Preferences API arbeitet asynchron, d.h. die Rückkehr zum Programm geschieht, bevor
die Daten tatsächlich in den Speicher geschrieben werden. Somit ist auch der Einsatz
langsamerer Speichermedien möglich (aber siehe Nachteile Schnelligkeit der Preferences
API).
12
3.4.
Nachteile
Beim Aufruf der get- Methoden ist die Angabe eines Defaultwertes zwingend erforderlich
(siehe nachfolgend die Signatur der get- Methoden). Somit wird der reibungslose Betrieb auch
bei Nichtverfügbarkeit des Speichermediums gewährleistet.
Signatur der get- Methode:
<Datentyp> get<Datentyp> ("<Präferenzbezeichnung>",<Standardwert>)
Die Defaultwerte sind aber nicht immer passend, besonders bei der Konfiguration der System
Daten. U.U. ist eine Applikation mit Defaultwerten nicht lauffähig, wenn z.B.
plattformspezifische Konfigurationen gesetzt werden müssen. Man denke nur an
Verzeichnispfade oder ähnliches, die mit Defaultwerten überhaupt nicht gesetzt werden
können.
Zudem wird dem Benutzer nicht mitgeteilt, ob der Defaultwert oder der „richtige“
gespeicherter Wert verwendet wird.
Die Preferences API ist nicht zur Speicherung der Applikationsdaten geeignet, wie z.B. vom
Benutzer erstellte Dokumente. Die API ist also nicht als Persistenzdienst bzw. Datenbank zu
gebrauchen, weil dieser Ansatz nur eine geringe Speicherkapazität (8,192 Bytes pro Wert) zur
Verfügung stellt.
Die Preferences API ist nicht sehr schnell, weshalb häufige Zugriffe auf die gespeicherten
Daten vermieden werden sollten.
Es ist dem Programmierer nicht möglich, die Konfigurationsdaten zu dokumentieren, da sie
ohne seinen Einfluss in das angeschlossene Speichermedium geschrieben werden.
Ebenfalls aus diesem Grund ist das Handling der Konfigurationsdaten sehr umständlich. Die
in den Bäumen abgelegten Konfigurationsdaten sind schlecht zu überblicken. Wird das
System an einen neuen Administrator abgegeben, so muss sich dieser in die vorhandene
Datenstruktur langwierig einarbeiten. Die bereits oben angeführte fehlende
Dokumentationsmöglichkeit kommt hier noch erschwerend hinzu. Denn es ist nicht
ersichtlich, welche Daten in welcher Klasse, bzw. welchen Knoten zu konfigurieren sind.
3.5.
Zusammenfassung
Die Preferences API stellt grundsätzlich einen komfortableren Ansatz zur Verwaltung von
Konfigurationsdaten dar als der Properties- Ansatz. Allerdings eignet sich die Preferences
API im Prinzip nur für Benutzerdaten, da die Unübersichtlichkeit der Datenspeicherung das
Konfigurieren erschweren und die zwingend erforderliche Defaultwertangabe, die zu
Systemabstürzen führen kann, den Einsatz für Systemdaten nicht empfehlen.
13
4. Introspektion
4.1.
Konzept
Der Begriff Introspektion heißt ins Deutsche übersetzt Selbstbeobachtung,
Erlebnisbeobachtung, Innenschau. Er bezeichnet die bewusste Beobachtung des eigenen
Erlebens und Verhaltens. U. a. wurde der Begriff im Zusammenhang mit der Psychoanalyse
gebraucht und bezeichnete eine Behandlungsform von psychisch kranken Menschen, die
selber Auskunft über ihre Leiden geben sollten, womit man sich einen Behandlungserfolg
versprach. Heute taucht der Begriff vor allem im philosophischen Kontext auf.
Man kann den Begriff auch auf die Informatik übertragen. Angewandt auf ein SoftwareSystem bedeutet Introspektion soviel wie ein System, dass über sich selber Auskunft gibt.
Z.B., um auf das Thema dieser Arbeit zurückzukommen, gibt das System Auskunft darüber,
welche Konfigurationen vorgenommen werden sollen, damit das System ordnungsgemäß
funktioniert.
4.2.
Funktionsweise
Die Introspektion arbeitet mit einer GUI, die dem Benutzer bzw. Systemadministrator anzeigt,
welche Attribute für das System konfiguriert werden sollen.
14
Abbildung 11 Ausschnitt aus der GUI des Online Brokers des SEBIS- Lehrstuhls
15
Die zu konfigurierenden Daten einer Klasse sind primitive Datentypen oder Objekte, d.h.
Klassen, die wiederum primitive Datentypen und Objekte als Konfigurationsdaten enthalten.
Dadurch entsteht in der GUI eine hierarchische Struktur.
In der GUI werden die Konfigurationsdaten durch Knoten repräsentiert. Diese Knoten werden
durch so genannte Konnektoren im Code erzeugt.
preferences.connect(new StringConnector() {
public String getKey() {
return "genericExceptionMessage";
}
public String getDescription() {
return "If this property is set, uncaught runtime exceptions
are indicated to the end user by displaying this
generic error message. Otherwise, a Java stack trace is
diplayed to the end user. The message value can also be
set to the empty string '' to suppress any error
output.";
}
public String getExample() {
return "Die Operation konnte nicht erfolgreich abgeschlossen
werden. Bitte kehren Sie mit der 'Zurueck'-Funktion
Ihres Browsers zur zuletzt angezeigten Seite zurueck.";
}
public void setValue(String newString) {
genericExceptionMessage = newString;
}
public String getValue() {
return genericExceptionMessage;
}
});
Abbildung 12 Dieser Code erzeugt einen Attribut- Knoten in der GUI
Neben diesen Konnektoren müssen in einer Klasse, deren Attribute konfiguriert werden
sollen, auch die connect()- Methode und initNew()- Methode des Connectable- Interfaces
implementiert werden.
Alle Funktionalitäten, die für die Introspektion nötig sind, werden vom connect- Package zur
Verfügung gestellt, insbesondere die Interfaces für die verschieden Typen von Konnektoren.
16
Connector
<<interface>>
+getKey () :String
+getDescription () :String
BooleanConntor
<<interface>>
StringConnector
<<interface>>
+getValue () :boolean
+setValue (boolean newValue) :void
PipelineConnector
<<interface>>
+getValue () :String
+setValue (String newString) :void
+getExample () :String
ObjectConnector
<<interface>>
ObjectsConnector
<<interface>>
+replaceObject (Object o) :void
+getClassName () :String
+getKey () :String
+setObjectFactory (ObjectFactory objectFactory) :void
+initNew () :void
Connectable
<<interface>>
+connect (Preferences preferences) :void
+initNew () :void
+getClassName () :String
+getObjectConnector (int position) :ObjectConnector
+getObjects () :List
+getClassName () :String
+setObjectFactory (ObjectFactory objectFactory) :void
+setCreateCopy (boolean createCopy) :void
+createCopy () :boolean
+initNew () :void
ObjectFactory
+createAndInitNewObject () :Object
+getClassName () :String
Abbildung 13 Interfaces und Klassen des Connect- Packages (Teilausschnitt)
17
Preferences
<<interface>>
+connect (ObjectConnector objectConnector) :void
+connect (ObjectsConnector objectsConnector) :void
+connect (PipelineConnector pipelineConnector) :void
+connect (StringConnector stringConnector) :void
+connect (BooleanConnector booleanConnector) :void
+connect (LogConnector logConnector) :void
+getSubPreferences (Connector connector) :Preferences
+handleEvent (NewObjectEvent e) :Preferences
+setReference (String name, Object reference) :void
+getReference (String name) :Object
Es besteht die Möglichkeit die Konfigurationsdaten in XML- Dateien abzuspeichern. Es
können einzelne Objekte abgespeichert werden oder auch ganze (Sub)Systeme.
4.3.
Vorteile
Die Introspektion zwingt zu einer anderen Sicht auf das System und unterstützt somit den
Fortschritt bei der Entwicklung großer Systeme. Durch die Anwendung der Introspektion
wird ein nachvollziehbarer Initialisierungsablauf erwirkt. Die Initialisierung der Objekte folgt
bei Anwendung der Introspektion einem bestimmten Muster bzw. Reihenfolge. Der Code
wird besser lesbar, da der Programmierer klar überblicken kann, was an welcher Stelle im
Code bezüglich der Konfiguration und Initialisierung von Objekten geschieht. Somit erhöht
sich auch das Verständnis für das Systemverhalten.
Der Administrator hat keine Berührung mehr mit dem Source- Code. Konfigurationen werden
in der GUI vorgenommen, in der die zu konfigurierenden Eigenschaften übersichtlich
gebündelt und hierarchisch angeordnet sind. Der Administrator behandelt das System also nur
aus seiner Administratorsicht, mit der internen Sicht muss er sich nicht mehr auseinander
setzen.
Die Introspektion ermöglicht es dem Programmierer ausführliche Dokumentationen der zu
konfigurierenden Eigenschaften direkt im Code zu erstellen. In der getDescription()- Methode
kann die Beschreibung der zu konfigurierenden Daten angegeben werden. Diese
ausführlichen Beschreibungen der Attribute, die in der GUI direkt dargestellt werden, helfen
auch dem Administrator bei seiner Arbeit, da er genau über die zu konfigurierenden Attribute
informiert wird. Somit wird die Arbeit beider Parteien erleichtert: Der Programmierer weiß,
wo und wie er konkret die zu konfigurierenden Attribute dokumentieren soll und der
Administrator bekommt die Dokumentation bequem angezeigt.
Durch die Option, einmal erstellte Konfigurationen in XML- Dateien abspeichern zu können,
ist es dem Administrator möglich, bestehende Konfigurationen wieder zu verwenden und
somit den Aufwand zur Systemkonfiguration zu reduzieren.
Die Anwendung der Introspektion erhöht die Konsistenz der Konfigurationsdaten, da keine zu
konfigurierenden Attribute vergessen werden können.
Der Code korrespondiert direkt mit den Konfigurationsdaten, die durch die GUI eingelesen
und im Code verarbeitet werden. Es besteht eine direkte Verbindung, anders als bei den zum
Code völlig separaten Properties- Dateien der Java Properties.
Die Introspektion stellt einen skalierbaren Ansatz dar. Schon für kleinere Systeme lohnt sich
die Anwendung dieses Verfahrens. Bei großen Systemen kommen die Anwendungsvorteile
vollends zur Entfaltung.
18
4.4.
Nachteile
Das Muster, nach dem der Initialisierungsworkflow abläuft, bietet neben den oben
angeführten Vorteilen der Übersichtlichkeit und des erhöhten Systemverständnisses
allerdings auch den Nachteil, dass der Programmierer an ein starres Schema gebunden ist, was
er bei seiner Programmierarbeit umsetzen muss. Es ist nicht möglich, von diesem Schema
abzuweichen, was vereinzelt zu Problemen führen kann.
In der GUI werden die Systemdaten vor dem Start der Applikation konfiguriert und stehen
dem System zur Ausführungszeit dann zur Verfügung. Konfigurationsdaten, die während der
Laufzeit erstellt werden, die so genannten Benutzerdaten, können in der GUI nicht
berücksichtigt werden.
4.5.
Zusammenfassung
Wie in obigen Abschnitt bereits angesprochen, eignet sich die Introspektion nur zur
Verwaltung von Systemdaten. Kurzfristige, zur Laufzeit anfallende Benutzerdaten können mit
diesem Verfahren nicht gemanagt werden. Für die Systemdaten allerdings bietet die
Introspektion einen äußert intelligenten und komfortablen Ansatz, dessen Vorteile absolut für
sich sprechen.
5. Resümee
Es ist wichtig, sich klar zu werden darüber, welche Art von Konfigurationsdaten man
verwalten möchte. Denn je nachdem ergeben sich andere Prämissen, die die Wahl des
Verfahrens bestimmen.
Der Java Properties- Ansatz eignet sich für System- wie für Benutzerdaten, wobei in beiden
Fällen die Nachteile dieses Ansatzes schwer ins Gewicht fallen. Allein die Einfachheit mag
für diesen Ansatz sprechen.
Der Java Preferences Ansatz eignet sich nach genauerer Betrachtung nur für die Verwaltung
von Benutzerdaten. Möchte man mit diesem Verfahren Systemdaten verwalten, muss man
sich mit einem schwierigen Handling aufgrund von Unübersichtlichkeit und fehlender
Dokumentation herumschlagen.
Die Introspektion stellt sich genau entgegengesetzt dar. Dieses Verfahren eignet sich nur für
die Verwaltung von Systemdaten, für die Verwaltung von Benutzerdaten werden keinerlei
Mechanismen angeboten. Allerdings beschränkt sich die Introspektion nicht nur auf die bloße
Verwaltung der Systemdaten, sondern bietet einen viel umfassenderen Ansatz, dessen
Implementierung sich vorteilhaft auf das gesamte System auswirkt, wie es bei den Vorteilen
ausführlich erläutert wurde.
Die Entscheidung bei der Entwicklung eines Software- Systems, welcher Ansatz oder welche
Kombination von Ansätzen zur Verwaltung der Konfigurationsdaten gewählt werden soll,
muss also in Abhängigkeit davon getroffen werden, welche Art von Konfigurationsdaten man
verwalten möchte. Je nachdem bieten sich unterschiedliche Optionen an. Es liegt dann an den
Entwicklern, eine für das geplante System und die zu erfüllende Aufgabe zufrieden stellende
Lösung auszuwählen.
19
Literaturverzeichnis
[java_01]
Artikel über Java Preferences von Ray Djajadinataz, Oktober 2001,
http://java.sun.com/developer/technicalArticles/releases/preferences/
[java_02]
JDK 1.4 Tutorial, The Preferences API Chapter 10 von Greg Travis,
veröffentlicht durch Manning Publications Company, Mai 2002
http://java.sun.com/developer/Books/javaprogramming/jdk14/javapch10.PDF
[java_03]
Artikel: „Using the Preferences API and Interfaces and Constants“, July 2003
http://java.sun.com/developer/JDCTechTips/2003/tt0715.html
[intro_01]
http://www.sign-lang.uni-hamburg.de/Projekte/PLex/PLex/lemmata/ILemma/Introspe.htm
20
Herunterladen