Skip to main content

skip to main content

developerWorks  >  Java technology  >

Magic with Merlin: Working with preferences

JSR 10, the Preferences API Specification, allows for the manipulation of preference and configuration data

developerWorks
Document options

Document options requiring JavaScript are not displayed

Discuss

Sample code


Rate this page

Help us improve this content


Level: Introductory

John Zukowski (jaz@zukowski.net), President, JZ Ventures, Inc.

01 Oct 2001

The addition of the java.util.prefs package to Java 1.4 (through JSR 10) lets you manipulate user preference data and configuration data by providing you with access to an implementation-specific registry (for example, the Windows Registry on Windows platforms). In this installment of Magic with Merlin, John Zukowski introduces you to the Preferences class and walks you through its use. He puts it all together with a sample program.

Have you ever needed to save configuration data for your program but didn't know where to store the data? While you can use property files or resource bundles for this information, the Java platform has never specified a standard place to store the files. That's all changed with JSR 10, which provides for the addition of the java.util.prefs package to the Java 1.4 API. The storage mechanism is an implementation-specific detail, but something that the programmer doesn't need to know or worry about. For Windows platforms, this location is the Windows Registry. You don't have free rein in the Registry, but you do have access from a common root node for all your applications.

Getting started

The aptly named Preferences class provides the basic framework for working with preferences. The class provides a series of static and abstract methods to work with one of the two sets of preferences: one for user preferences and one for system preferences. Using the static methods, you get a platform-specific implementation, like the WindowsPreferences class; then you can use the abstract methods implemented by that platform-specific implementation to do the work.

It's good practice to group a program's preferences by package to avoid naming conflicts with other applications. When you ask for a Preferences object, you simply pass along the package name. In the case of a non-static method, you can pass a reference to yourself (this), and the program will determine the package for you, as shown in Listing 1.


Listing 1. Fetching a Preferences object from non-static method
Preferences userPrefs = Preferences.userNodeForPackage(this);
Preferences sysPrefs = Preferences.systemNodeForPackage(this);

If, however, you're in a static method, you have to get the root node and provide the package yourself, as shown in Listing 2.


Listing 2. Fetching Preferences object from static method
Preferences userPrefs = Preferences.userRoot().node("/net/zukowski/ibm");
Preferences sysPrefs = Preferences.systemRoot().node("/net/zukowski/ibm");

After you have the node to work with, you can set, get, remove, and dump settings at your leisure. Just think of the Preferences object as one big key-value hashtable that structures the keys in a tree-like structure. It is not part of the Collections Framework, though (for more on the Collections Framework, see Resources).



Back to top


Writing data

We'll begin by looking at how to store preferences. The Preferences class provides a series of put() methods, shown below, for storing values. In addition to basic string support, you can store booleans, doubles, floats, integers, longs, and byte arrays (think serialization). The helper methods take the appropriate data type and do the necessary conversions to store the data as a string.

  • put(String key, String value)
  • putBoolean(String key, boolean value)
  • putByteArray(String key, byte value[])
  • putDouble(String key, double value)
  • putFloat(String key, float value)
  • putInt(String key, int value)
  • putLong(String key, long value)

All the put() methods return a void. If the storage mechanism is unavailable, a BackingStoreException will be thrown.

Note: The key for a specific preference is limited to a length of Preferences.MAX_KEY_LENGTH (80) characters, while values are limited to Preferences.MAX_VALUE_LENGTH (8192) characters.



Back to top


Reading data

Getting specific preferences is done through a series of get() methods, shown below. Like writing, there is a different method for each of the supported data types. One difference with getting, though, is that you must provide a default value in the event that the backing store is unavailable, or for when something hasn't been saved yet. This requirement ensures at least reasonable default settings for your program.

  • get(String key, String default)
  • getBoolean(String key, boolean default)
  • getByteArray(String key, byte default[])
  • getDouble(String key, double default)
  • getFloat(String key, float default)
  • getInt(String key, int default)
  • getLong(String key, long default)

If you're not sure of the preference names, you can find a list of the keys associated with a node with the keys() method. This method returns a String[] of nodes. Besides getting and storing individual preferences and getting a list of keys, you can also remove nodes and values with clear(), remove(), and removeNode().



Back to top


Dumping data

If you want to save and restore preferences outside of the system-provided backing storage, you can do so in an XML-formatted document. You can either export a node with exportNode() or a whole subtree with exportSubtree(). The information is stored in UTF-8 format. Then, when you want to restore the information, use the importPreferences() method.



Back to top


Listening in

Curiosity may have killed the cat, but if you are interested in finding out when preferences change, you can register a NodeChangeListener or PreferenceChangeListener with little concern to your immediate health. The NodeChangeListener works to notify you when nodes are added and removed, while the PreferenceChangeListener tells you of value changes. These follow the basic JavaBeans component event handling structure with add/removeNodeChangeListener(NodeChangeListener) and add/removePreferenceChangeListener() methods. Basically, you implement the listener, then register the listener, and you'll find about future changes.



Back to top


Complete example

That's really all there is to it. Listing 3 provides a complete example for you to try out the new capabilities (also available for download from Resources). The program cleans up after itself, so if you want to find the values in the registry, comment out the cleanup code at the end.


Listing 3. Complete example
package net.zukowski.ibm;

import java.io.*;
import java.util.prefs.*;

public class Prefs {
  public static void main(String args[]) {
    String denominations[] = 
      {"One", "Two", "Five", "Ten", "Twenty"};
    String pictures[] = 
      {"Washington", "Jefferson", "Lincoln", "Hamilton", "Jackson"};

    NodeChangeListener nodeChangeListener = 
      new NodeChangeListener() {
        public void childAdded(NodeChangeEvent event) {
          Preferences parent = event.getParent();
          Preferences child  = event.getChild();
          System.out.println(parent.name() + " has a new child " +
            child.name());
        }
        public void childRemoved(NodeChangeEvent event) {
          Preferences parent = event.getParent();
          Preferences child  = event.getChild();
          System.out.println(parent.name() + " lost a child " +
            child.name());
        }
      };

    PreferenceChangeListener preferenceChangeListener = 
      new PreferenceChangeListener() {
        public void preferenceChange(PreferenceChangeEvent event) {
          String key = event.getKey();
          String value = event.getNewValue();
          Preferences node = event.getNode();
          System.out.println(node.name() + " now has a value of " + 
            value + " for " + key);
        }
      };

    // Look up user root
    Preferences prefs = 
      Preferences.userRoot().node("/net/zukowski/ibm");

    // Add listeners
    prefs.addNodeChangeListener(nodeChangeListener);
    prefs.addPreferenceChangeListener(preferenceChangeListener);

    // Save a bunch of key-value pairs
    for (int i=0, n=denominations.length; i < n; i++) {
      prefs.put(denominations[i], pictures[i]);
    }

    // Display all the entries
    try {
      String keys[] = prefs.keys();
      for (int i=0, n=keys.length; i < n; i++) {
        System.out.println(keys[i] + ": " + prefs.get(keys[i], "Unknown"));
      }
    } catch (BackingStoreException e) {
      System.err.println("Unable to read backing store: " + e);
    }

    // Create child
    Preferences child = Preferences.userRoot().node("/net/zukowski/ibm/foo");

    // Save to XML file
    try {
      FileOutputStream fos = new FileOutputStream("prefs.out");
      prefs.exportNode(fos);
    } catch (Exception e) {
      System.err.println("Unable to export nodes: " + e);
    }

    // Clean up
    try {
      prefs.removeNode();
    } catch (BackingStoreException e) {
      System.err.println("Unable to access backing store: " + e);
    }

  }
}




Back to top


Download

DescriptionNameSizeDownload method
Sample codej-prefs.zip1 KBHTTP
Information about download methods


Resources



About the author

Author photo

John Zukowski conducts strategic Java consulting with JZ Ventures, Inc. and serves as the resident guru for a number of jGuru's community-driven Java FAQs. His latest books are Java Collections and Definitive Guide to Swing for Java 2 (2nd ed) from Apress. Reach him at jaz@zukowski.net.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top