1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package java.util.prefs;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.net.MalformedURLException;
23 import java.util.ServiceLoader;
26 * An instance of the class {@code Preferences} represents one node in a
27 * preference tree, which provides a mechanism to store and access configuration
28 * data in a hierarchical way. Two hierarchy trees are maintained, one for
29 * system preferences shared by all users and the other for user preferences
30 * specific to the user. {@code Preferences} hierarchy trees and data are stored
31 * in an implementation-dependent back-end.
33 * Every node has one name and one unique absolute path following the same
34 * notational conventions as directories in a file system. The root node's
35 * name is "", and other node name strings cannot contain the slash character
36 * and cannot be empty. The root node's absolute path is "/", and all other
37 * nodes' absolute paths are constructed in the standard way: <parent's
38 * absolute path> + "/" + <node's name>. Since the set of nodes forms a
39 * tree with the root node at its base, all absolute paths start with the slash
40 * character. Every node has one relative path to each of its ancestors. The
41 * relative path doesn't start with slash: it equals the node's absolute path
42 * with leading substring removed corresponding to the ancestor's absolute path
45 * Modification to preferences data may be asynchronous, which means that
46 * preference update method calls may return immediately instead of blocking.
47 * The {@code flush()} and {@code sync()} methods force the back-end to
48 * synchronously perform all pending updates, but the implementation is
49 * permitted to perform the modifications on the underlying back-end data
50 * at any time between the moment the request is made and the moment the
51 * {@code flush()} or {@code sync()} method returns. Please note that if the JVM
52 * exits normally, the implementation must assure all modifications are
53 * persisted implicitly.
55 * When invoking a method that retrieves preferences, the user must provide
56 * a default value. The default value is returned when the preferences cannot
57 * be found or the back-end is unavailable. Some other methods will throw
58 * {@code BackingStoreException} when the back-end is unavailable.
61 * Preferences can be exported to and imported from an XML files. These
62 * documents must have an XML DOCTYPE declaration:
64 * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
66 * This system URI is not really accessed by network, it is only a
67 * identification string. Visit the DTD location to see the actual format
70 * There must be a concrete {@code PreferencesFactory} type for every concrete
71 * {@code Preferences} type developed. Every J2SE implementation must provide a
72 * default implementation for every supported platform, and must also provide a
73 * means of replacing the default implementation. This implementation uses the
74 * system property {@code java.util.prefs.PreferencesFactory} to determine which
75 * preferences implementation to use.
77 * The methods of this class are thread-safe. If multiple JVMs are using the
78 * same back-end concurrently, the back-end won't be corrupted, but no other
79 * behavior guarantees are made.
81 * @see PreferencesFactory
85 public abstract class Preferences {
87 * Maximum size in characters allowed for a preferences key.
89 public static final int MAX_KEY_LENGTH = 80;
92 * Maximum size in characters allowed for a preferences name.
94 public static final int MAX_NAME_LENGTH = 80;
97 * Maximum size in characters allowed for a preferences value.
99 public static final int MAX_VALUE_LENGTH = 8192;
102 private static final RuntimePermission PREFS_PERM = new RuntimePermission("preferences");
104 //factory used to get user/system prefs root
105 private static final PreferencesFactory factory = findPreferencesFactory();
107 private static PreferencesFactory findPreferencesFactory() {
108 // Try the system property first...
109 PreferencesFactory result = ServiceLoader.loadFromSystemProperty(PreferencesFactory.class);
110 if (result != null) {
113 // Then use ServiceLoader for META-INF/services/...
114 for (PreferencesFactory impl : ServiceLoader.load(PreferencesFactory.class, null)) {
117 // Finally return a default...
118 return new FilePreferencesFactoryImpl();
122 * Default constructor, for use by subclasses only.
124 protected Preferences() {
129 * Gets the absolute path string of this preference node.
131 * @return the preference node's absolute path string.
133 public abstract String absolutePath();
136 * Returns the names of all children of this node or an empty array if this
137 * node has no children.
139 * @return the names of all children of this node.
140 * @throws BackingStoreException
141 * if backing store is unavailable or causes an operation
143 * @throws IllegalStateException
144 * if this node has been removed.
146 public abstract String[] childrenNames() throws BackingStoreException;
149 * Removes all preferences of this node.
151 * @throws BackingStoreException
152 * if backing store is unavailable or causes an operation
154 * @throws IllegalStateException
155 * if this node has been removed.
157 public abstract void clear() throws BackingStoreException;
160 * Exports all of the preferences of this node to a XML document using the
161 * given output stream.
163 * This XML document uses the UTF-8 encoding and is written according to the
164 * DTD in its DOCTYPE declaration, which is the following:
167 * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
170 * <i>Please note that (unlike the methods of this class that don't concern
171 * serialization), this call is not thread-safe.</i>
175 * the output stream to write the XML-formatted data to.
176 * @throws IOException
177 * if an error occurs while exporting.
178 * @throws BackingStoreException
179 * if the backing store is unavailable or causes an operation
181 * @throws IllegalStateException
182 * if this node has been removed.
184 public abstract void exportNode(OutputStream ostream) throws IOException, BackingStoreException;
187 * Exports all of the preferences of this node and all its descendants to a
188 * XML document using the given output stream.
190 * This XML document uses the UTF-8 encoding and is written according to the
191 * DTD in its DOCTYPE declaration, which is the following:
194 * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
197 * <i>Please note that (unlike the methods of this class that don't concern
198 * serialization), this call is not thread-safe.</i>
202 * the output stream to write the XML-formatted data to.
203 * @throws IOException
204 * if an error occurs while exporting.
205 * @throws BackingStoreException
206 * if the backing store is unavailable or causes an operation
208 * @throws IllegalStateException
209 * if this node has been removed.
211 public abstract void exportSubtree(OutputStream ostream) throws IOException,
212 BackingStoreException;
215 * Forces all pending updates to this node and its descendants to be
216 * persisted in the backing store.
218 * If this node has been removed, the invocation of this method only flushes
219 * this node, not its descendants.
222 * @throws BackingStoreException
223 * if the backing store is unavailable or causes an operation
226 public abstract void flush() throws BackingStoreException;
229 * Gets the {@code String} value mapped to the given key or its default
230 * value if no value is mapped or no backing store is available.
232 * Some implementations may store default values in backing stores. In this
233 * case, if there is no value mapped to the given key, the stored default
238 * the preference key.
240 * the default value, which will be returned if no value is
241 * mapped to the given key or no backing store is available.
242 * @return the preference value mapped to the given key.
243 * @throws IllegalStateException
244 * if this node has been removed.
245 * @throws NullPointerException
246 * if the parameter {@code key} is {@code null}.
248 public abstract String get(String key, String deflt);
251 * Gets the {@code boolean} value mapped to the given key or its default
252 * value if no value is mapped, if the backing store is unavailable, or if
253 * the value is invalid.
255 * The only valid values are the {@code String} "true", which represents
256 * {@code true} and "false", which represents {@code false}, ignoring case.
259 * Some implementations may store default values in backing stores. In this
260 * case, if there is no value mapped to the given key, the stored default
265 * the preference key.
267 * the default value, which will be returned if no value is
268 * mapped to the given key, if the backing store is unavailable,
269 * or if the value is invalid.
270 * @return the boolean value mapped to the given key.
271 * @throws IllegalStateException
272 * if this node has been removed.
273 * @throws NullPointerException
274 * if the parameter {@code key} is {@code null}.
276 public abstract boolean getBoolean(String key, boolean deflt);
279 * Gets the {@code byte} array value mapped to the given key or its default
280 * value if no value is mapped, if the backing store is unavailable, or if
281 * the value is an invalid string.
283 * To be valid, the value string must be Base64-encoded binary data. The
284 * Base64 encoding is as defined in <a
285 * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>, section 6.8.
288 * Some implementations may store default values in backing stores. In this
289 * case, if there is no value mapped to the given key, the stored default
294 * the preference key.
296 * the default value, which will be returned if no value is
297 * mapped to the given key, if the backing store is unavailable,
298 * or if the value is invalid.
299 * @return the byte array value mapped to the given key.
300 * @throws IllegalStateException
301 * if this node has been removed.
302 * @throws NullPointerException
303 * if the parameter {@code key} is {@code null}.
305 public abstract byte[] getByteArray(String key, byte[] deflt);
308 * Gets the {@code double} value mapped to the given key or its default
309 * value if no value is mapped, if the backing store is unavailable, or if
310 * the value is an invalid string.
312 * To be valid, the value string must be a string that can be converted to a
313 * {@code double} by {@link Double#parseDouble(String)
314 * Double.parseDouble(String)}.
316 * Some implementations may store default values in backing stores. In this
317 * case, if there is no value mapped to the given key, the stored default
322 * the preference key.
324 * the default value, which will be returned if no value is
325 * mapped to the given key, if the backing store is unavailable, or if the
327 * @return the double value mapped to the given key.
328 * @throws IllegalStateException
329 * if this node has been removed.
330 * @throws NullPointerException
331 * if the parameter {@code key} is {@code null}.
333 public abstract double getDouble(String key, double deflt);
336 * Gets the {@code float} value mapped to the given key or its default value
337 * if no value is mapped, if the backing store is unavailable, or if the
338 * value is an invalid string.
340 * To be valid, the value string must be a string that can be converted to a
341 * {@code float} by {@link Float#parseFloat(String)
342 * Float.parseFloat(String)}.
345 * Some implementations may store default values in backing stores. In this
346 * case, if there is no value mapped to the given key, the stored default
351 * the preference key.
353 * the default value, which will be returned if no value is
354 * mapped to the given key, if the backing store is unavailable, or if the
356 * @return the float value mapped to the given key.
357 * @throws IllegalStateException
358 * if this node has been removed.
359 * @throws NullPointerException
360 * if the parameter {@code key} is {@code null}.
362 public abstract float getFloat(String key, float deflt);
365 * Gets the {@code int} value mapped to the given key or its default value
366 * if no value is mapped, if the backing store is unavailable, or if the
367 * value is an invalid string.
369 * To be valid, the value string must be a string that can be converted to
370 * an {@code int} by {@link Integer#parseInt(String)
371 * Integer.parseInt(String)}.
374 * Some implementations may store default values in backing stores. In this
375 * case, if there is no value mapped to the given key, the stored default
380 * the preference key.
382 * the default value, which will be returned if no value is
383 * mapped to the given key, if the backing store is unavailable,
384 * or if the value is invalid.
385 * @return the integer value mapped to the given key.
386 * @throws IllegalStateException
387 * if this node has been removed.
388 * @throws NullPointerException
389 * if the parameter {@code key} is {@code null}.
391 public abstract int getInt(String key, int deflt);
394 * Gets the {@code long} value mapped to the given key or its default value
395 * if no value is mapped, if the backing store is unavailable, or if the
396 * value is an invalid string.
398 * To be valid, the value string must be a string that can be converted to a
399 * {@code long} by {@link Long#parseLong(String) Long.parseLong(String)}.
402 * Some implementations may store default values in backing stores. In this
403 * case, if there is no value mapped to the given key, the stored default
408 * the preference key.
410 * the default value, which will be returned if no value is
411 * mapped to the given key, if the backing store is unavailable,
412 * or if the value is invalid.
413 * @return the long value mapped to the given key.
414 * @throws IllegalStateException
415 * if this node has been removed.
416 * @throws NullPointerException
417 * if the parameter {@code key} is {@code null}.
419 public abstract long getLong(String key, long deflt);
422 * Imports all the preferences from an XML document using the given input
425 * This XML document uses the UTF-8 encoding and must be written according
426 * to the DTD in its DOCTYPE declaration, which must be the following:
429 * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
432 * <i>Please note that (unlike the methods of this class that don't concern
433 * serialization), this call is not thread-safe.</i>
437 * the input stream to read the data from.
438 * @throws InvalidPreferencesFormatException
439 * if the data read from the given input stream is not from a
440 * valid XML document.
441 * @throws IOException
442 * if an error occurs while importing.
443 * @throws SecurityException
444 * if {@code RuntimePermission("preferences")} is denied by a
447 public static void importPreferences (InputStream istream) throws InvalidPreferencesFormatException, IOException {
449 if (istream == null){
450 throw new MalformedURLException("Inputstream cannot be null");
452 XMLParser.importPrefs(istream);
456 * Returns whether this is a user preference node.
458 * @return {@code true}, if this is a user preference node, {@code false} if
459 * this is a system preference node.
461 public abstract boolean isUserNode();
464 * Returns all preference keys stored in this node or an empty array if no
467 * @return the list of all preference keys of this node.
468 * @throws BackingStoreException
469 * if the backing store is unavailable or causes an operation
471 * @throws IllegalStateException
472 * if this node has been removed.
474 public abstract String[] keys() throws BackingStoreException;
477 * Returns the name of this node.
479 * @return the name of this node.
481 public abstract String name();
484 * Returns the preference node with the given path name. The path name can
485 * be relative or absolute. The requested node and its ancestors will
486 * be created if they do not exist.
488 * The path is treated as relative to this node if it doesn't start with a
489 * slash, otherwise it will be treated as an absolute path.
493 * the path name of the requested preference node.
494 * @return the requested preference node.
495 * @throws IllegalStateException
496 * if this node has been removed.
497 * @throws IllegalArgumentException
498 * if the path name is invalid.
499 * @throws NullPointerException
500 * if the given path is {@code null}.
502 public abstract Preferences node(String path);
505 * Returns whether the preference node with the given path name exists. The
506 * path is treated as relative to this node if it doesn't start with a slash,
507 * otherwise it is treated as an absolute path.
509 * Please note that if this node has been removed, an invocation of this
510 * node will throw an {@code IllegalStateException} unless the given path is
511 * an empty string, which will return {@code false}.
515 * the path name of the preference node to query.
516 * @return {@code true}, if the queried preference node exists, {@code false}
518 * @throws IllegalStateException
519 * if this node has been removed and the path is not an empty
521 * @throws IllegalArgumentException
522 * if the path name is invalid.
523 * @throws NullPointerException
524 * if the given path is {@code null}.
525 * @throws BackingStoreException
526 * if the backing store is unavailable or causes an operation
529 public abstract boolean nodeExists(String path) throws BackingStoreException;
532 * Returns the parent preference node of this node or {@code null} if this
533 * node is the root node.
535 * @return the parent preference node of this node.
536 * @throws IllegalStateException
537 * if this node has been removed.
539 public abstract Preferences parent();
542 * Adds a new preference to this node using the given key and value or
543 * updates the value if a preference with the given key already exists.
546 * the preference key to be added or updated.
548 * the preference value for the given key.
549 * @throws NullPointerException
550 * if the given key or value is {@code null}.
551 * @throws IllegalArgumentException
552 * if the given key's length is bigger than {@code
553 * MAX_KEY_LENGTH} or the value's length is bigger than {@code
555 * @throws IllegalStateException
556 * if this node has been removed.
558 public abstract void put(String key, String value);
561 * Adds a new preference with a {@code boolean} value to this node using the
562 * given key and value or updates the value if a preference with the given
563 * key already exists.
566 * the preference key to be added or updated.
568 * the preference {@code boolean} value for the given key.
569 * @throws NullPointerException
570 * if the given key is {@code null}.
571 * @throws IllegalArgumentException
572 * if the given key's length is bigger than {@code
574 * @throws IllegalStateException
575 * if this node has been removed.
577 public abstract void putBoolean(String key, boolean value);
580 * Adds a new preference to this node using the given key and the string
581 * form of the given value or updates the value if a preference with the
582 * given key already exists.
584 * The string form of the value is the Base64-encoded binary data of the
585 * given byte array. The Base64 encoding is as defined in <a
586 * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>, section 6.8.
590 * the preference key to be added or updated.
592 * the preference value for the given key.
593 * @throws NullPointerException
594 * if the given key or value is {@code null}.
595 * @throws IllegalArgumentException
596 * if the given key's length is bigger than {@code
597 * MAX_KEY_LENGTH} or value's length is bigger than three
598 * quarters of {@code MAX_KEY_LENGTH}.
599 * @throws IllegalStateException
600 * if this node has been removed.
602 public abstract void putByteArray(String key, byte[] value);
605 * Adds a new preference to this node using the given key and {@code double}
606 * value or updates the value if a preference with the
607 * given key already exists.
609 * The value is stored in its string form, which is the result of invoking
610 * {@link Double#toString(double) Double.toString(double)}.
614 * the preference key to be added or updated.
616 * the preference value for the given key.
617 * @throws NullPointerException
618 * if the given key is {@code null}.
619 * @throws IllegalArgumentException
620 * if the given key's length is bigger than {@code
622 * @throws IllegalStateException
623 * if this node has been removed.
625 public abstract void putDouble(String key, double value);
628 * Adds a new preference to this node using the given key and {@code float}
629 * value or updates the value if a preference with the
630 * given key already exists.
632 * The value is stored in its string form, which is the result of invoking
633 * {@link Float#toString(float) Float.toString(float)}.
637 * the preference key to be added or updated.
639 * the preference value for the given key.
640 * @throws NullPointerException
641 * if the given key is {@code null}.
642 * @throws IllegalArgumentException
643 * if the given key's length is bigger than {@code
645 * @throws IllegalStateException
646 * if this node has been removed.
648 public abstract void putFloat(String key, float value);
651 * Adds a new preference to this node using the given key and {@code int}
652 * value or updates the value if a preference with the
653 * given key already exists.
655 * The value is stored in its string form, which is the result of invoking
656 * {@link Integer#toString(int) Integer.toString(int)}.
660 * the preference key to be added or updated.
662 * the preference value for the given key.
663 * @throws NullPointerException
664 * if the given key is {@code null}.
665 * @throws IllegalArgumentException
666 * if the given key's length is bigger than {@code
668 * @throws IllegalStateException
669 * if this node has been removed.
671 public abstract void putInt(String key, int value);
674 * Adds a new preference to this node using the given key and {@code long}
675 * value or updates the value if a preference with the
676 * given key already exists.
678 * The value is stored in its string form, which is the result of invoking
679 * {@link Long#toString(long) Long.toString(long)}.
683 * the preference key to be added or updated.
685 * the preference value for the given key.
686 * @throws NullPointerException
687 * if the given key is {@code null}.
688 * @throws IllegalArgumentException
689 * if the given key's length is bigger than {@code
691 * @throws IllegalStateException
692 * if this node has been removed.
694 public abstract void putLong(String key, long value);
697 * Removes the preference mapped to the given key from this node.
700 * the key of the preference to be removed.
701 * @throws NullPointerException
702 * if the given key is {@code null}.
703 * @throws IllegalStateException
704 * if this node has been removed.
706 public abstract void remove(String key);
709 * Removes this preference node with all its descendants. The removal won't
710 * necessarily be persisted until the method {@code flush()} is invoked.
712 * @throws BackingStoreException
713 * if the backing store is unavailable or causes an operation
715 * @throws IllegalStateException
716 * if this node has been removed.
717 * @throws UnsupportedOperationException
718 * if this is a root node.
720 public abstract void removeNode() throws BackingStoreException;
723 * Registers a {@code NodeChangeListener} instance for this node, which will
724 * handle {@code NodeChangeEvent}s. {@code NodeChangeEvent}s will be fired
725 * when a child node has been added to or removed from this node.
728 * the listener to be registered.
729 * @throws NullPointerException
730 * if the given listener is {@code null}.
731 * @throws IllegalStateException
732 * if this node has been removed.
734 public abstract void addNodeChangeListener(NodeChangeListener ncl);
737 * Registers a {@code PreferenceChangeListener} instance for this node,
738 * which will handle {@code PreferenceChangeEvent}s. {@code
739 * PreferenceChangeEvent}s will be fired when a preference has been added
740 * to, removed from, or updated for this node.
743 * the listener to be registered.
744 * @throws NullPointerException
745 * if the given listener is {@code null}.
746 * @throws IllegalStateException
747 * if this node has been removed.
749 public abstract void addPreferenceChangeListener (PreferenceChangeListener pcl);
752 * Removes the given {@code NodeChangeListener} instance from this node.
755 * the listener to be removed.
756 * @throws IllegalArgumentException
757 * if the given listener is {@code null}.
758 * @throws IllegalStateException
759 * if this node has been removed.
761 public abstract void removeNodeChangeListener (NodeChangeListener ncl);
764 * Removes the given {@code PreferenceChangeListener} instance from this
768 * the listener to be removed.
769 * @throws IllegalArgumentException
770 * if the given listener is {@code null}.
771 * @throws IllegalStateException
772 * if this node has been removed.
774 public abstract void removePreferenceChangeListener (PreferenceChangeListener pcl);
777 * Synchronizes the data of this preference node and its descendants with
778 * the back-end preference store. Any changes found in the back-end data
779 * should be reflected in this node and its descendants, and at the same
780 * time any local changes to this node and descendants should be persisted.
782 * @throws BackingStoreException
783 * if the backing store is unavailable or causes an operation
785 * @throws IllegalStateException
786 * if this node has been removed.
788 public abstract void sync() throws BackingStoreException;
791 * Returns the system preference node for the package of the given class.
792 * The absolute path of the returned node is one slash followed by the given
793 * class's full package name, replacing each period character ('.') with
794 * a slash. For example, the absolute path of the preference associated with
795 * the class Object would be "/java/lang". As a special case, the unnamed
796 * package is associated with a preference node "/<unnamed>". This
797 * method will create the node and its ancestors as needed. Any nodes created
798 * by this method won't necessarily be persisted until the method {@code
799 * flush()} is invoked.
803 * @return the system preference node for the package of the given class.
804 * @throws NullPointerException
805 * if the given class is {@code null}.
806 * @throws SecurityException
807 * if the {@code RuntimePermission("preferences")} is denied by
810 public static Preferences systemNodeForPackage (Class<?> c) {
812 return factory.systemRoot().node(getNodeName(c));
816 * Returns the root node of the system preference hierarchy.
818 * @return the system preference hierarchy root node.
819 * @throws SecurityException
820 * if the {@code RuntimePermission("preferences")} is denied by
823 public static Preferences systemRoot() {
825 return factory.systemRoot();
828 //check the RuntimePermission("preferences")
829 private static void checkSecurity() {
830 SecurityManager manager = System.getSecurityManager();
832 manager.checkPermission(PREFS_PERM);
838 * Returns the user preference node for the package of the given class.
839 * The absolute path of the returned node is one slash followed by the given
840 * class's full package name, replacing each period character ('.') with
841 * a slash. For example, the absolute path of the preference associated with
842 * the class Object would be "/java/lang". As a special case, the unnamed
843 * package is associated with a preference node "/<unnamed>". This
844 * method will create the node and its ancestors as needed. Any nodes created
845 * by this method won't necessarily be persisted until the method {@code
846 * flush()} is invoked.
850 * @return the user preference node for the package of the given class.
851 * @throws NullPointerException
852 * if the given class is {@code null}.
853 * @throws SecurityException
854 * if the {@code RuntimePermission("preferences")} is denied by
857 public static Preferences userNodeForPackage (Class<?> c) {
859 return factory.userRoot().node(getNodeName(c));
862 //parse node's absolute path from class instance
863 private static String getNodeName(Class<?> c){
864 Package p = c.getPackage();
868 return "/"+p.getName().replace('.', '/');
872 * Returns the root node of the user preference hierarchy.
874 * @return the user preference hierarchy root node.
875 * @throws SecurityException
876 * if the {@code RuntimePermission("preferences")} is denied by
879 public static Preferences userRoot() {
881 return factory.userRoot();
885 * Returns a string representation of this node. The format is "User/System
886 * Preference Node: " followed by this node's absolute path.
888 * @return the string representation of this node.
891 public abstract String toString();