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
|