Advanced Java Services | Benutzereinstellungen speichern |
Benutzereinstellungen können auf viele verschiedene Weisen gespeichert werden. Hier geht es um die Speicherung mit Hilfe der Klasse Preferences. Man unterscheidet zwischen der Benutzerumgebung und der Systemumgebung. Zur Benutzerumgebung gehört etwa der Name der zuletzt bearbeiteten Dateien, während man den Zustand der verwendeten Umgebung, die (global) für jeden Benutzer vorhanden ist zur Systemungebung zählt.
Die Klasse Preferences ist abstrakt und hat statische Factorymethoden. Für die Benutzerumgebung verwendet man die statische Methode userRoot(), für die Systemumgebung die statische Methode systemRoot(). Ähnlich wie bei den Klassen Properties oder Hashmap verfügt Properties u.a. über put(), get() und remove() - Methoden.
Jedes Betriebssystem hat dafür andere Orte, die aber für das jeweilige Os standardisiert sind.
Unter Mac werden die Benutzereinstellungen im Verzeichnis /Library/Preferences/ gespeichert. Mac verwendet dabei XML-Dateien.
Unter Linux/Unix werden die Benutzereinstellungen im Verzeichnis $HOME/.java/.userPrefs gespeichert. Die Systemeinstellungen dagegen werden gespeichert unter /etc/.java/systemPrefs. Auch hier ist das Format XML.
Unter Windows werden die Einträge in der Registry gespeichert. Die JVM hat also beschränkten Zugriff auf die Registry.
Speicherort der Benutzereinstellungen | HKEY_CURRENT_USER\Software\JavaSoft\Prefs |
Speicherort der Systemeinstellungen (64bit JVM) |
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs |
Speicherort der Systemeinstellungen (32bit JVM in Windows 64 System) |
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\JavaSoft\Prefs |
Für die SystemPreferences muß jedoch der Schlüssel Prefs von Hand erstellt werden und dazu muß
Um die Plattformunabhängigkeit herzustellen gibt es in rt.jar versteckte plattformabhängige Implementierungen. In allen Betriebssystemen hat Preferences die Unterklasse AbstractPreferences. Darunter sind jeweils die in der API nicht sichtbaren bestriebssystemabhängigen Klassen FileSystemPreferences, MacOSXPreferences und WindowsPreferences. Das ist einer der Tricks mit dem Java die Plattformunabhängigkeit herstellt. Für Windows kann man das sehen indem man in der PocketAPI nach Preferences sucht und dann sich das Innenleben der Klasse WindowsPreferences anschaut.
// User-Preferences Preferences userRoot = Preferences.userRoot(); System.out.println(userRoot); // User Preference Node: / // Einen Schlüssel eintragen userRoot.put("last used", "eclipse, firefox"); userRoot.put("rootkey", "rootvalue"); // Einen Unterknoten eintragen Preferences subnode = userRoot.node("subnode"); subnode.put("subkey1", "subvalue1"); subnode.put("subkey2", "subvalue2"); // Schlüssel auslesen System.out.println(subnode.get("subkey1", null)); // Einen Schlüssel löschen subnode.remove("subkey2"); System.out.println("subkey2 removed"); subnode.remove("subkey1"); System.out.println("subkey1 removed"); // Unterknoten entfernen try { subnode.removeNode(); // 'entfernt sich selbst' // die änderung wird erst sichtbar, wenn die registry // geschlossen wird und neu geöffnet wird } catch(BackingStoreException ex) { ex.printStackTrace(); }
Die Einträge in der registry
Genauso.
Da das Vorgehen für beide Arten von Preferences dasselbe ist, wird hier nur auf die User-Preferences eingegangen.
Sehr einfach mit eportSubTree
// User-Preferences Preferences userRoot = Preferences.userRoot(); // Einen Schlüssel eintragen userRoot.put("last used", "eclipse, firefox"); userRoot.put("key", "value"); Preferences subnode = userRoot.node("subnode"); subnode.put("subkey1", "subvalue1"); subnode.put("subkey2", "subvalue2"); try(FileOutputStream fos = new FileOutputStream("prefs.xml");) { userRoot.exportSubtree(fos); } catch(IOException | BackingStoreException ex) { ex.printStackTrace(); }
Die XML-Datei
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"> <preferences EXTERNAL_XML_VERSION="1.0"> <root type="user"> <map> <entry key="last used" value="eclipse, firefox"/> <entry key="key" value="value"/> </map> <node name="subnode"> <map> <entry key="subkey1" value="subvalue1"/> <entry key="subkey2" value="subvalue2"/> </map> </node> </root> </preferences>
Ebenso einfach mit importPreferences(InputStream is)
try(FileInputStream fis = new FileInputStream("prefs.xml");) { Preferences.importPreferences(fis); } catch(IOException | InvalidPreferencesFormatException ex) { ex.printStackTrace(); }
Um den Unterknoten zu sehen muß eine geöffnete Registry geschlossen und wieder geöffnet werden.
Ähnlich wie bei graphischen Oberflächen gibt es zwei Listenerklassen mit denen man Änderungen in den Knoten und Ästen des Preferencebaums verfolgen kann.
Mit einem NodeChangeListener kann man auf das Hinzufügen oder Entfernen eines Knoterns reagieren, wie das folgende Beispiel zeigt. Wichtig ist hier flush zu verwenden, damit die Änderungen gleich realisiert werden. Der Listener wird erst gerufen, wenn die Änderungen in kraft getreten sind.
private static void nodeChangeListenerDemo() { // User-Preferences Preferences userRoot = Preferences.userRoot(); MyNodeChangeListener ncl = new MyNodeChangeListener(); userRoot.addNodeChangeListener(ncl); // adden Preferences childnode = userRoot.node("childnode"); try { userRoot.flush(); childnode.removeNode(); userRoot.flush(); } catch(BackingStoreException ex1) { ex1.printStackTrace(); } System.out.println("end nodeChangeListenerDemo"); }
Der Listener
class MyNodeChangeListener implements NodeChangeListener { @Override public void childAdded(NodeChangeEvent e) { System.out.println(e); System.out.println("source = " + e.getSource()); System.out.println("parent = " + e.getParent()); System.out.println("child = " + e.getChild()); } @Override public void childRemoved(NodeChangeEvent e) { System.out.println(e); System.out.println("source = " + e.getSource()); System.out.println("parent = " + e.getParent()); System.out.println("child = " + e.getChild()); } }
Die Ausgabe
java.util.prefs.AbstractPreferences$NodeAddedEvent[source=User Preference Node: /] source = User Preference Node: / parent = User Preference Node: / child = User Preference Node: /childnode java.util.prefs.AbstractPreferences$NodeRemovedEvent[source=User Preference Node: /] source = User Preference Node: / parent = User Preference Node: / child = User Preference Node: /childnode
Mit einem PreferenceChangeListener kann man auf das Hinzufügen oder Entfernen eines Schlüssels reagieren wie das folgende Beispiel zeigt. Wichtig ist hier flush zu verwenden, damit die Änderungen gleich realisiert werden. Der Listener wird erst gerufen, wenn die Änderungen in kraft getreten sind.
static void preferenceChangeListenerDemo() { Preferences userRoot = Preferences.userRoot(); userRoot.addPreferenceChangeListener( new MyPreferenceChangeListener()); try { userRoot.put("the-key", "the-value"); // ruft den Listener auch wenn der key schon mit demselben value vorhanden ist userRoot.flush(); userRoot.remove("key"); // ein doppeltes remove loest keine Exception aus userRoot.flush(); userRoot.put("the-key", "new-value"); // ruft den Listener auch wenn der key schon mit demselben value vorhanden ist userRoot.flush(); } catch(BackingStoreException ex) { ex.printStackTrace(); } }
Der Listener
static class MyPreferenceChangeListener implements PreferenceChangeListener { @Override public void preferenceChange(PreferenceChangeEvent e) { System.out.println("node = " + e.getNode()); System.out.println("key = " + e.getKey()); System.out.println("new value = " + e.getNewValue()); } }
Die Ausgabe
node = User Preference Node: / key = the-key new value = the-value node = User Preference Node: / key = key new value = null node = User Preference Node: / key = the-key new value = new-value