UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

134 lines (130 loc) 17.4 kB
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><head><title>The Preferences API</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="The Preferences API"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-11-SECT-6"/>The Preferences API</h1></div></div></div><p>The Java Preferences API accommodates the need to store both system and per-user configuration data persistently across executions of the Java VM. The Preferences API is like a portable version of the Windows registry, a mini-database in which you can keep small amounts of information, accessible to all applications. Entries are stored as name/value pairs, where the values may be of several standard types including strings, numbers, Booleans, and even short byte arrays. We should stress that the Preferences API is not intended to be used as a true database and you can’t store large amounts of data in it.</p><p>Preferences are stored logically in a tree. A preferences object is a node in the tree located by a unique path. You can think of preferences as files in a directory structure; within the file are stored one or more name/value pairs. To store or retrieve items, you ask for a preferences object for the correct path. Here is an example; we’ll explain the node lookup shortly:</p><a id="I_11_tt741"/><pre class="programlisting"> <code class="n">Preferences</code> <code class="n">prefs</code> <code class="o">=</code> <code class="n">Preferences</code><code class="o">.</code><code class="na">userRoot</code><code class="o">().</code><code class="na">node</code><code class="o">(</code><code class="s">"oreilly/learningjava"</code><code class="o">);</code> <code class="n">prefs</code><code class="o">.</code><code class="na">put</code><code class="o">(</code><code class="s">"author"</code><code class="o">,</code> <code class="s">"Niemeyer"</code><code class="o">);</code> <code class="n">prefs</code><code class="o">.</code><code class="na">putInt</code><code class="o">(</code><code class="s">"edition"</code><code class="o">,</code> <code class="mi">4</code><code class="o">);</code> <code class="n">String</code> <code class="n">author</code> <code class="o">=</code> <code class="n">prefs</code><code class="o">.</code><code class="na">get</code><code class="o">(</code><code class="s">"author"</code><code class="o">,</code> <code class="s">"unknown"</code><code class="o">);</code> <code class="kt">int</code> <code class="n">edition</code> <code class="o">=</code> <code class="n">prefs</code><code class="o">.</code><code class="na">getInt</code><code class="o">(</code><code class="s">"edition"</code><code class="o">,</code> <code class="o">-</code><code class="mi">1</code><code class="o">);</code></pre><p>In addition to the <code class="literal">String</code> and <code class="literal">int</code> type accessors, there are the following get methods for other types: <code class="literal">getLong()</code>, <code class="literal">getFloat()</code>, <code class="literal">getDouble()</code>, <code class="literal">getByteArray()</code>, and <code class="literal">getBoolean()</code>. Each of these get methods takes a key name and default value to be used if no value is defined. And, of course, for each get method, there is a corresponding “put” method that takes the name and a value of the corresponding type. Providing defaults in the get methods is mandatory. The intent is for applications to function even if there is no preference information or if the storage for it is not available, as we’ll discuss later.</p><p><a id="I_indexterm11_id748086" class="indexterm"/> <a id="I_indexterm11_id748092" class="indexterm"/>Preferences are stored in two separate trees: system preferences and user preferences. <span class="emphasis"><em>System preferences</em></span> are shared by all users of the Java installation. But <span class="emphasis"><em>user preferences</em></span> are maintained separately for each user; each user sees his or her own preference information. In our example, we used the static method <a id="I_indexterm11_id748109" class="indexterm"/><code class="literal">userRoot()</code> to fetch the root node (preference object) for the user preferences tree. We then asked that node to find the child node at the path <span class="emphasis"><em>oreilly/learningjava</em></span>, using the <a id="I_indexterm11_id748124" class="indexterm"/><code class="literal">node()</code> method. The corresponding <a id="I_indexterm11_id748135" class="indexterm"/><code class="literal">systemRoot()</code> method provides the system root node.</p><p>The <code class="literal">node()</code> method accepts either a relative or an absolute path. A relative path asks the node to find the path relative to itself as a base. We also could have gotten our node this way:</p><a id="I_11_tt742"/><pre class="programlisting"> <code class="n">Preferences</code> <code class="n">prefs</code> <code class="o">=</code> <code class="n">Preferences</code><code class="o">.</code><code class="na">userRoot</code><code class="o">().</code><code class="na">node</code><code class="o">(</code><code class="s">"oreilly"</code><code class="o">).</code><code class="na">node</code><code class="o">(</code><code class="s">"learningjava"</code><code class="o">);</code></pre><p>But <code class="literal">node()</code> also accepts an absolute path, in which case the base node serves only to designate the tree that the path is in. We could use the absolute path <span class="emphasis"><em>/oreilly/learningjava</em></span> as the argument to any <code class="literal">node()</code> method and reach our preferences object.</p><div class="sect2" title="Preferences for Classes"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-11-SECT-6.1"/>Preferences for Classes</h2></div></div></div><p><a id="I_indexterm11_id748190" class="indexterm"/> <a id="I_indexterm11_id748196" class="indexterm"/> <a id="I_indexterm11_id748203" class="indexterm"/> <a id="I_indexterm11_id748211" class="indexterm"/> <a id="I_indexterm11_id748222" class="indexterm"/> <a id="I_indexterm11_id748228" class="indexterm"/> <a id="I_indexterm11_id748235" class="indexterm"/>Java is an object-oriented language, and so it’s natural to wish to associate preference data with classes. In <a class="xref" href="ch12.html" title="Chapter 12. Input/Output Facilities">Chapter 12</a>, we’ll see that Java provides special facilities for loading resource files associated with class files. The Preferences API follows this pattern by associating a node with each Java package. Its convention is simple: the node path is just the package name with the dots (.) converted to slashes (/). All classes in the package share the same node.</p><p>You can get the preference object node for a class using the static <a id="I_indexterm11_id748258" class="indexterm"/><code class="literal">Preferences.userNodeForPackage()</code> or <a id="I_indexterm11_id748269" class="indexterm"/><code class="literal">Preferences.systemNodeForPackage()</code> methods, which take a <code class="literal">Class</code> as an argument and return the corresponding package node for the user and system trees, respectively. For example:</p><a id="I_11_tt743"/><pre class="programlisting"> <code class="n">Preferences</code> <code class="n">datePrefs</code> <code class="o">=</code> <code class="n">Preferences</code><code class="o">.</code><code class="na">systemNodeForPackage</code><code class="o">(</code> <code class="n">Date</code><code class="o">.</code><code class="na">class</code> <code class="o">);</code> <code class="n">Preferences</code> <code class="n">myPrefs</code> <code class="o">=</code> <code class="n">Preferences</code><code class="o">.</code><code class="na">userNodeForPackage</code><code class="o">(</code> <code class="n">MyClass</code><code class="o">.</code><code class="na">class</code> <code class="o">);</code> <code class="n">Preferences</code> <code class="n">morePrefs</code> <code class="o">=</code> <code class="n">Preferences</code><code class="o">.</code><code class="na">userNodeForPackage</code><code class="o">(</code> <code class="n">myObject</code><code class="o">.</code><code class="na">getClass</code><code class="o">()</code> <code class="o">);</code></pre><p>Here, we’ve used the <code class="literal">.class</code> construct to refer to the <code class="literal">Class</code> object for the <code class="literal">Date</code> class in the system tree and to our own <code class="literal">MyClass</code> class in the user tree. The <code class="literal">Date</code> class is in the <code class="literal">java.util</code> package, so we’ll get the node <span class="emphasis"><em>/java/util</em></span> in that case. You can get the <code class="literal">Class</code> for any object instance using the <code class="literal">getClass()</code> method.</p></div><div class="sect2" title="Preferences Storage"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-11-SECT-6.2"/>Preferences Storage</h2></div></div></div><p><a id="I_indexterm11_id748355" class="indexterm"/> <a id="I_indexterm11_id748362" class="indexterm"/> <a id="I_indexterm11_id748372" class="indexterm"/>There is no need to “create” nodes. When you ask for a node, you get a preferences object for that path in the tree. If you write something to it, that data is eventually placed in persistent storage, called the backing store. The <span class="emphasis"><em>backing store</em></span> is the implementation-dependent storage mechanism used to hold the preference data. All the put methods return immediately, and no guarantees are made as to when the data is actually stored. You can force data to the backing store explicitly using the <a id="I_indexterm11_id748391" class="indexterm"/><code class="literal">flush()</code> method of the <a id="I_indexterm11_id748402" class="indexterm"/><code class="literal">Preferences</code> class. Conversely, you can use the <a id="I_indexterm11_id748413" class="indexterm"/><code class="literal">sync()</code> method to guarantee that a preferences object is up-to-date with respect to changes placed into the backing store by other applications or threads. Both <code class="literal">flush()</code> and <code class="literal">sync()</code> throw a <code class="literal">BackingStoreException</code> if data cannot be read or written for some reason.</p><p>You don’t have to create nodes, but you can test for the existence of a data node with the <a id="I_indexterm11_id748446" class="indexterm"/><code class="literal">nodeExists()</code> method, and you can remove a node and all its children with the <a id="I_indexterm11_id748457" class="indexterm"/><code class="literal">remove</code><code class="literal">Node()</code> method. To remove a data item from a node, use the <code class="literal">remove()</code> method, specifying the key; or you can remove all the data from a node with the <a id="I_indexterm11_id748483" class="indexterm"/><code class="literal">clear()</code> method (which is not the same as removing the node).</p><p>Although the details of the backing store are implementation-dependent, the Preferences API provides a simple import/export facility that can read and write parts of a preference tree to an XML file. (The format for the file is available at <span class="emphasis"><em>http://java.sun.com/dtd/</em></span>.) A preference object can be written to an output stream with the <a id="I_indexterm11_id748508" class="indexterm"/><code class="literal">exportNode()</code> method. The <a id="I_indexterm11_id748519" class="indexterm"/><code class="literal">exportSubtree()</code> method writes the node and all its children. Going the other way, the static <a id="I_indexterm11_id748530" class="indexterm"/><code class="literal">Preferences.importPreferences()</code> method can read the XML file and populate the appropriate tree with its data. The XML file records whether it is user or system preferences, but user data is always placed into the current user’s tree, regardless of who generated it.</p><p>It’s interesting to note that because the import mechanism writes directly to the tree, you can’t use this as a general data-to-XML storage mechanism (other APIs play that role). Also, although we said that the implementation details are not specified, it’s interesting how things really work in the current implementation. On some systems, Java creates a directory hierarchy for each tree at <span class="emphasis"><em>$JAVA_HOME/jre/.systemPrefs</em></span> and <span class="emphasis"><em>$HOME/.java/.userPrefs</em></span>, respectively. In each directory, there is an XML file called <span class="emphasis"><em>prefs.xml</em></span> corresponding to that node.<a id="I_indexterm11_id748565" class="indexterm"/></p></div><div class="sect2" title="Change Notification"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-11-SECT-6.3"/>Change Notification</h2></div></div></div><p><a id="idx10633" class="indexterm"/> <a id="idx10665" class="indexterm"/>Often your application should be notified if changes are made to the preferences while it’s running. You can get updates on preference changes using the <a id="I_indexterm11_id748607" class="indexterm"/><code class="literal">PreferenceChangeListener</code> and <a id="I_indexterm11_id748618" class="indexterm"/><a id="I_indexterm11_id748624" class="indexterm"/><code class="literal">NodeChangeListener</code> interfaces. These interfaces are examples of <span class="emphasis"><em>event listener</em></span> interfaces, and we’ll see many examples of these in Chapters <a class="xref" href="ch16.html" title="Chapter 16. Swing">16</a> through <a class="xref" href="ch18.html" title="Chapter 18. More Swing Components">18</a>. We’ll also talk about the general pattern later in this chapter in the section <a class="xref" href="ch11s08.html" title="Observers and Observables">Observers and Observables</a>. For now, we’ll just say that by registering an object that implements <code class="literal">PreferenceChangeListener</code> with a node, you can receive updates on added, removed, and changed preference data for that node. The <code class="literal">NodeChangeListener</code> allows you to be told when child nodes are added to or removed from a specific node. Here is a snippet that prints all the data changes affecting our <span class="emphasis"><em>/oreilly/learningjava</em></span> node:</p><a id="I_11_tt744"/><pre class="programlisting"> <code class="n">Preferences</code> <code class="n">prefs</code> <code class="o">=</code> <code class="n">Preferences</code><code class="o">.</code><code class="na">userRoot</code><code class="o">().</code><code class="na">node</code><code class="o">(</code><code class="s">"/oreilly/learningjava"</code><code class="o">);</code> <code class="n">prefs</code><code class="o">.</code><code class="na">addPreferenceChangeListener</code><code class="o">(</code> <code class="k">new</code> <code class="n">PreferenceChangeListener</code><code class="o">()</code> <code class="o">{</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">preferenceChange</code><code class="o">(</code><code class="n">PreferenceChangeEvent</code> <code class="n">e</code><code class="o">)</code> <code class="o">{</code> <code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code><code class="s">"Value: "</code> <code class="o">+</code> <code class="n">e</code><code class="o">.</code><code class="na">getKey</code><code class="o">()</code> <code class="o">+</code> <code class="s">" changed to "</code><code class="o">+</code> <code class="n">e</code><code class="o">.</code><code class="na">getNewValue</code><code class="o">()</code> <code class="o">);</code> <code class="o">}</code> <code class="o">}</code> <code class="o">);</code></pre><p>In brief, this example listens for changes to preferences and prints them. If this example isn’t immediately clear, it should be after you’ve read about events in <a class="xref" href="ch16.html" title="Chapter 16. Swing">Chapter 16</a> and beyond.<a id="I_indexterm11_id748700" class="indexterm"/><a id="I_indexterm11_id748707" class="indexterm"/></p></div></div></body></html>