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.


Wo werden die Einstellungen gespeichert

Jedes Betriebssystem hat dafür andere Orte, die aber für das jeweilige Os standardisiert sind.


Mac

Unter Mac werden die Benutzereinstellungen im Verzeichnis /Library/Preferences/ gespeichert. Mac verwendet dabei XML-Dateien.


Unix/Linux

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.


Windows

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.

preferences-hierarchie.jpg missing


User-Preferences eintragen und löschen
// 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

registry-1.jpg missing

registry-2.jpg missing


System-Preferences eintragen

Genauso.


Preferences ex- und importieren in und aus einer XML-Datei

Da das Vorgehen für beide Arten von Preferences dasselbe ist, wird hier nur auf die User-Preferences eingegangen.


Exportieren

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>

Importieren

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.


NodeChangeListener und PreferenceChangeListener

Ähnlich wie bei graphischen Oberflächen gibt es zwei Listenerklassen mit denen man Änderungen in den Knoten und Ästen des Preferencebaums verfolgen kann.


NodeChangeListener

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

PreferenceChangeListener

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