All of the methods that modify preference data are permitted to operate asynchronously. They may return immediately, and changes will eventually propagate to the persistent backing store. The flush method can be used to force updates to the backing store.
The methods in the Preferences class may be invoked concurrently by multiple threads in a single JVM without the need for external synchronization, and the results will be equivalent to some serial execution. If this class is used concurrently by multiple JVMs that store their preference data in the same backing store, the data store will not be corrupted, but no other guarantees are made concerning the consistency of the preference data.
For more details, choose from the following links:
Prior to the introduction of the Preferences API, developers could choose to manage preference and configuration data in an ad hoc fashion, by using the Properties API or the JNDI API as described below.
Often, preference and configuration data was stored in properties files, accessed through the java.util.Properties API. However, there are no standards as to where such files should reside on disk, or what they should be called. Using this mechanism, it is extremely difficult to backup a user's preference data, or transfer it from one machine to another. As the number of applications increases, the possibility of file name conflicts increases. Also, this mechanism is of no help on platforms that lack a local disk, or where it is desirable that the data be stored in an external data store (such as an enterprise-wide LDAP directory service).
Less frequently, developers stored user preference and configuration data in a directory service, accessed through the Java Naming and Directory Interface (JNDI) API. Unlike the Properties API, JNDI allows the use of arbitrary data stores (back-end neutrality). While JNDI is extremely powerful, it is also rather large, consisting of 5 packages and 83 classes. JNDI provides no policy as to where in the directory name space the preference data should be stored, or in which name space.
Neither Properties nor JNDI provide a simple, ubiquitous, back-end neutral preferences management facility. The Preferences API does provide such a facility, combining the simplicity of Properties with the back-end neutrality of JNDI. It provides sufficient built-in policy to prevent name clashes, foster consistency, and encourage robustness in the face of inaccessibility of the backing data store.
The material contained in this section is not part of the Preferences API specification, instead it is intended to provide some examples of how the Preferences API might be used.
The following examples illustrate how you might obtain the Preferences objects (system and user) pertaining to the enclosing class. These examples would work only inside instance methods.
Note that static final fields, rather than inline String
literals, are used for the key names (NUM_ROWS
and
NUM_COLS
). This reduces the likelihood of runtime bugs
from typographical errors in key names.
Note also that reasonable defaults are provided for each of the preference values obtained. These defaults will be returned if no preference value has been set, or if the backing store is inaccessible.
package com.acme.widget; import java.util.prefs.*; public class Gadget { // Preference keys for this package private static final String NUM_ROWS = "num_rows"; private static final String NUM_COLS = "num_cols"; void foo() { Preferences prefs = Preferences.userNodeForPackage(Gadget.class); int numRows = prefs.getInt(NUM_ROWS, 40); int numCols = prefs.getInt(NUM_COLS, 80); ... } }
The above example obtains per-user preferences. If a single,
per-system value were desired, the first line in foo
would be replaced by:
Preferences prefs = Preferences.systemNodeForPackage(Gadget.class);
The examples in the prior section illustrate obtaining Preferences objects pertaining to the enclosing class, and work inside instance methods. In a static method (or static initializer), you need to explicitly provide the name of the package:
Static String ourNodeName = "/com/acme/widget"; static void foo() { Preferences prefs = Preferences.userRoot().node(ourNodeName); ... }
It is always acceptable to obtain a system preferences object once, in a static initializer, and use it whenever system preferences are required:
static Preferences prefs = Preferences.systemRoot().node(ourNodeName);
In general, it is acceptable to do the same thing for a user
preferences object, but not if the code in question is to be used
in a server, wherein multiple users will be running concurrently or
serially. In such a system, userNodeForPackage
and
userRoot
will return the appropriate node for the
calling user, thus it's critical that calls to
userNodeForPackage
or userRoot
be made
from the appropriate thread at the appropriate time. If a piece of
code may eventually be used in such a server environment, it is
good, conservative practice to obtain user preferences objects
immediately before they are used, as in the prior example.
The preferences API does not provide database like "transactions" wherein multiple preferences are modified atomically. Occasionally, it is necessary to modify two or more preferences as a unit. For example, suppose you are storing the x and y coordinates where a window is to be placed. The only way to achieve atomicity is to store both values in a single preference. Many encodings are possible. Here's a simple one:
int x, y; ... prefs.put(POSITION, x + "," + y);
When such a "compound preference" is read, it must be decoded. For robustness, allowances should be made for a corrupt (unparseable) value:
static int X_DEFAULT = 50, Y_DEFAULT = 25; void baz() { String position = prefs.get(POSITION, X_DEFAULT + "," + Y_DEFAULT); int x, y; try { int i = position.indexOf(','); x = Integer.parseInt(coordinates.substring(0, i)); y = Integer.parseInt(position.substring(i + 1)); } catch(Exception e) { // Value was corrupt, just use defaults x = X_DEFAULT; y = Y_DEFAULT; } ... }
Typical application code has no need to know whether the backing store is available. It should almost always be available, but if it isn't, the code should continue to execute using default values in place of preference values from the backing store. Very rarely, some advanced program might want to vary its behavior (or simply refuse to run) if the backing store were unavailable. Following is a method that determines whether the backing store is available by attempting to modify a preference value and flush the result to the backing store.
private static final String BACKING_STORE_AVAIL = "BackingStoreAvail"; private static boolean backingStoreAvailable() { Preferences prefs = Preferences.userRoot().node("<temporary>"); try { boolean oldValue = prefs.getBoolean(BACKING_STORE_AVAIL, false); prefs.putBoolean(BACKING_STORE_AVAIL, !oldValue); prefs.flush(); } catch(BackingStoreException e) { return false; } return true; }