Home : Course Map : Chapter 10 : Java :
The Preferences API
JavaTech
Course Map
Chapter 10

Introduction
Vector/Enumeration
Hashtable,HashMap
   Properties
Collections

Iterator/ArrayList
Generics
Preferences API
  Demo 1
Concurrency Utils
Enumerated Type
Arrays Class
String Tools
  String
  StringBuffer
  StringBuilder
  StringTokenizer
  String.split()

Calendar,Date,Time
  Demo 2
  Demo 3

Other Utilities
Exercises

    Supplements
Performance
Benchmarks
     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

When you write an program for yourself, you can always modify it directly to implement the characteristics that you desire. However if you write it for more than one user, you can be sure that some of the users will wish to modify the default choices for such items as the text display, e.g. the font style, size, color, etc..

For example, an application might provide a "Font" menu in which the user can select a font style and size from a list of choices. This will be appreciatd by your users ut they will be disappointed to find that the application reverts to your hard-coded choices each time the application restarts.

You can get around this shortcoming by, for example, providing a configuration file that the user could edit. However, that approach is prone to errors (e.g. misspellings). Also, the file must be located by the program and this can be a problem if the program is to be used on multiple types of platforms.

If you expect multiple users of the same program on the same machine then you must also deal with multiple sets of configuration preferences, which in turn can mean dealing with multiple configuration files.

A valuable technique used in the past to handle the formatting, reading, and writing of configuration information is the java.util.Properties class, mentioned in a previous section. The Properties class has been a part of Java since version 1.0. Still, the problem of knowing where to store the configuration file remains.

The Preferences API provides a systematic way to handle program preference configurations. The java.util.prefs package and has been a part of Java since version 1.4. Amazingly, it provides the tools to easily store and retrieve user preferences that persist across application invocations (i.e., the preferences are "remembered" from one run of an application to the next). It automatically maintains separate preference lists for multiple users, and transparently handles storing the preferences information (the "configuration file," if you wish) in a way that the programmer need never worry about.

The actual place that the preferences data is stored is platform dependent, but the Preferences API transparently handles access to the preferences information in a platform independent manner. Said another way, source code that uses the Preferences API behaves the same way on any platform and hides the fact that the preferences data might be stored differently on different platforms. The API even includes a platform independent import/export facility so that preferences can be backed up or moved from one machine to another.

Ease of Use

The online documentation for the Preferences API is unfortunately not written in a tutorial manner and makes it can sound harder to use than it is. The most important class in java.util.prefs is the Preferences class.

That documentation refers to this Preferences class as "a node in a hierarchical collection of preference data" and continues with "there are two separate trees of preference nodes, one for user preferences and one for system preferences." We use a simple example below to explain what this all menas. .

One first obtains a Preferences object by using the static method userNodeForPackage(). This method requires a Class object as its only parameter. The system then determines the package in which the specified class resides and returns the Preferences object to be used to access user preferences information for that package. Since all applications generally have their own unique package names, preferences based on package names do not conflict with other packages.

The userNodeForPackage() method returns a different Preferences object for each user. So multiple users of the same application on the same machine do not conflict with each other. That is, different users, as distinguished by different user.name system property values, have separate storage locations for their preferences data.

The description so far may not sound simple and easy to use yet, but the API really is simple, as the following code snippets illustrate. So far, we have described one line of code to obtain the Preferences object

Preferences prefs = Preferences.userNodeForPackage (getClass ());

To store a preferences item takes one more line of code - the prefs.put() method. Retrieving a stored item the next time the application is run, takes just one line of code - the prefs.get() method.

Preferences information is stored as key/value pairs by the Preferences object, similar to the way system properties are stored. The call to the put() method must provide both the name of the key under which the item is to be stored and its value, both as String objects. An example is

prefs.put ("color", "red");

Here the key "color" is stored with the value "red". You choose the key names to be whatever makes sense. Then, the next time the same Java application is run by the same user, the previously stored value can be retrieved with

String preferred_color = prefs.get ("color", "some-default-value");

All of the Preferences methods that retrieve values require a second parameter that provides a default value in case nothing is found under the named key. In that way, the application can continue running with default values, although perhaps with slightly reduced functionality.

In addition to the general put() and get() methods described above, there are convenience methods to store and retrieve int values:

prefs.putInt ("width", 500);
int preferred_width = prefs.get ("width", 700);

Here, the integer value 500 is stored under the key name width. Later the value stored is retrieved and loaded into the int variable preferred_width, using a default value of 700. Similar convenience methods exist for other primitive types:

  • boolean - putBoolean() and getBoolean()
  • long - putLong() and getLong()
  • float - putFloat() and getFloat()
  • double - putDouble() and getDouble())

There are even convenience methods to put and get a byte array. Another useful method is clear() which removes all key/value pairs in the Preferences node.

Preferences Demo

The simple command-line Java application PrefsDemo shown below demonstrates the basics of the Preferences API. The application stores a single key/value pair in which the key name is "PrefsValue". On the command line you pass one of the three arguments "put", "get", or "clear".

For example,

C:\Java\prefs\> java PrefsDemo put George

Putting `George' into prefs
Number of puts since clear is 1

Then we can access the preference with

C:\Java\prefs\>java PrefsDemo get

Got PrefsValue `George' from prefs

These command line parameters may put the next value supplied on the command line into the preferences node, or they may get and echo a previously stored value or clear all key/value pairs. Because we always provide a default value when attempting to retrieve stored preferences, as is required, any attempt to retrieve a value before one is stored results in a suitable default value being returned.

You can play with the put, get, and clear parameters to get a feel for how the Preferences API works. In addition to the "PrefsValue" key name, the demo also uses the putInt() and getInt() convenience methods to keep track of the total number of "put" operations that have been performed. This counter is incremented by one each time a "put" is performed and, more importantly, the value is maintained across application invocations, even across recompilations and reboots!

If the first command line parameter is "clear", then all key/value pairs are removed from the preferences node, effectively resetting the counter back to 0.

(See next subsection for a discussion of the "export" parameter case.)

PrefsDemo.java

We also offer the standard accessory files that one should include when distributing files. Here README.txt describes the program and gives instructions on how to use the files. The Window .bat command files go as follows: build.bat does the compilation, clean.bat removes the class files when no longer needed, and run.bat executes the program.

 

import java.util.prefs.*;

/**
  * Simple demonstration of the most common usage of the Java
  * Preferences API using the user and package based storage
  * node. This app uses the user tree to avoid collisions with
  * other users and uses the package name, as is conventional,
  * to avoid collisions with other applications in other packages.
  *
  * This is a simple command-line application. It stores only one
  * key/value pair, in which key is the string "PrefsValue".
  *
  * Argument 1 may be either "get", "clear", or "put".
  *
  * If "get", the value stored under the key "PrefsValue" is
  * fetched and displayed.
  *
  * If "clear", all prefs items for this package are cleared.
  *
  * If "put", the second command-line argument provides the value
  * to be stored. If the second argument is null, a suitable default
  * value is used.
  *
  * If "get" is requested the first time this application is run
  * or after a "clear" operation, a suitable default value is
  * returned.
  *
 **/
public class PrefsDemo {
  // Define constants for the three possible operations.
  private static final int GET   = 1;
  private static final int CLEAR = 2;
  private static final int PUT   = 3;

  /** Constructs the PrefsDemo application. **/
  public PrefsDemo (String[] args) {

    // Get the preferences node for this user and this package.
    Preferences prefs = Preferences.userNodeForPackage (getClass ());

    // Decode the command-line arguments.
    String command  = null;
    String param2   = null;
    String param3   = null;
    String newvalue = null;
    boolean export  = false;

    System.err.println ("");
    if (args.length == 0) {
        System.err.println ("No command given, assuming 'get'");
        command = "get";
    }
    else if (args.length == 1) {
        command = args[0];
    }
    else if (args.length == 2) {
        command = args[0];
        param2  = args[1];
    }
    else if (args.length == 3) {
        command = args[0];
        param2  = args[1];
        param3  = args[2];
    }

    // Turn the string commands into ints so they can be used
    // in a switch.
    int operation;
    if (command.equals ("get")) {
        operation = GET;
    }
    else if (command.equals ("clear")) {
        operation = CLEAR;
    }
    else if (command.equals ("put")) {
        operation = PUT;
        newvalue =
          param2!=null ? param2 : "you forgot the value, dummy";
    }
    else {
        System.err.println
          ("Don't understand command '" + command + "', assuming 'get'");
        operation = GET;
    }

    // See if the 2nd parameter (for GET and CLEAR) or
    // 3rd parameter (for PUT) is the string "export".
    if (operation == GET || operation == CLEAR) {
        export = "export".equalsIgnoreCase (param2);
    }
    else if (operation == PUT) {
        export = "export".equalsIgnoreCase (param3);
    }

    // Do the operation requested by the command-line argument(s).
    switch (operation) {
      case CLEAR:
        System.err.println ("Clearing preferences");
        try {
          prefs.clear ();
        }
        catch (BackingStoreException bse) {
          System.err.println (bse);
        }
        break;
      case GET:
        String prefs_value = prefs.get ("PrefsValue", "default value");
        System.err.println
          ("Got PrefsValue `" + prefs_value + "' from prefs");
        break;
      case PUT:
        System.err.println ("Putting `" + newvalue + "' into prefs");
        prefs.put ("PrefsValue", newvalue);
        int num_puts = prefs.getInt ("num_puts", 0);
        prefs.putInt ("num_puts", num_puts+1);
        System.err.println
          ("Number of puts since clear is " + (num_puts+1));
        break;
    } // switch

    if (export) {
        try {
          prefs.exportNode (System.out);
        }
        catch (java.io.IOException ioe) {
          System.err.println (ioe);
        }
        catch (BackingStoreException bse) {
          System.err.println (bse);
        }
    }

  } // ctor

  public static void main (String[] args) {
    new PrefsDemo (args);
  } // main

}   // class PrefsDemoApp

 

Exporting and Importing Preferences

The Preferences API also provides export and import facilities for moving the settings on one machine to another . The exportNode() method creates an XML document (see a brief discussion of XML in Chapter 21) on the specified OutputStream and is transferred and imported to the same application running on another machine. Because of the cross-platform nature of XML, the preferences XML file can even be moved to the same Java application running on a completely different platform.

If the last parameter to the PrefsDemo application is "export", then the app uses exportNode() to output the preferences XML file onto System.out. In this way, you can examine the tree and node structure of the preferences tree.

Use of the importPreferences() method to read in an XML preferences file is not shown in the demo application, but its use is straightforward after referring to the online documentation.

System Preferences

The discussion above refers to user preferences - i.e., preferences stored for a particular user. The Preferences API also supports system-level preferences that apply to all users. To reach the system-wide portion of the preferences storage system, use

Preferences prefs = Preferences.systemNodeForPackage (getClass ());

System-level preferences would typically be used is to store system-wide defaults when an application is first installed.

Other Services

Our brief discussion above describes most of the features of the Preferences API, including the most typical usage. There are a few other services provided by the API, such as listeners that listen for preference value changes. These additional features are less likely to be used, especially in a scientific application. You can find good documentation of these features in the online Java 2 API specifications for the java.util.prefs package.

Where is the Preferences Data Really Stored?

The actual storage of preferences information is implementation dependent. We programmers don't need to know where the data is stored, as long as the Preferences API always works the same on all platforms, which it does. The important thing to know is that the data really is stored persistently somewhere.

Nevertheless, the curious might want to know where the data is really stored. In practice, the Sun J2SE implementation on Windows platforms utilizes the Windows Registry as can be verified by examining the registry.

On Solaris and Linux, the user node is normally stored in a hidden file in the user's home directory. Other implementations might use directory servers or SQL databases. It really is implementation dependent. And it really is unimportant.

References & Web Resources

 

Latest update: Nov. 17, 2004

              Tech
ArbitaryPrecision
   BigInteger
  
BigDecimal
Bit Handling
Exercises

           Physics
Data Gen&Analysis

  Demo 1
  Demo 2
Exercises

  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.