186 * intentional or unintentional denial of service due to a locked node.
187 * To avoid deadlock, a node is <i>never</i> locked by a thread that
188 * holds a lock on a descendant of that node.
189 */
190 protected final Object lock = new Object();
191
192 /**
193 * Creates a preference node with the specified parent and the specified
194 * name relative to its parent.
195 *
196 * @param parent the parent of this preference node, or null if this
197 * is the root.
198 * @param name the name of this preference node, relative to its parent,
199 * or {@code ""} if this is the root.
200 * @throws IllegalArgumentException if {@code name} contains a slash
201 * ({@code '/'}), or {@code parent} is {@code null} and
202 * name isn't {@code ""}.
203 */
204 protected AbstractPreferences(AbstractPreferences parent, String name) {
205 if (parent==null) {
206 if (!name.equals(""))
207 throw new IllegalArgumentException("Root name '"+name+
208 "' must be \"\"");
209 this.absolutePath = "/";
210 root = this;
211 } else {
212 if (name.indexOf('/') != -1)
213 throw new IllegalArgumentException("Name '" + name +
214 "' contains '/'");
215 if (name.equals(""))
216 throw new IllegalArgumentException("Illegal name: empty string");
217
218 root = parent.root;
219 absolutePath = (parent==root ? "/" + name
220 : parent.absolutePath() + "/" + name);
221 }
222 this.name = name;
223 this.parent = parent;
224 }
225
226 /**
227 * Implements the {@code put} method as per the specification in
228 * {@link Preferences#put(String,String)}.
229 *
230 * <p>This implementation checks that the key and value are legal,
231 * obtains this preference node's lock, checks that the node
232 * has not been removed, invokes {@link #putSpi(String,String)}, and if
233 * there are any preference change listeners, enqueues a notification
234 * event for processing by the event dispatch thread.
235 *
831 * lock is dropped prior to breaking {@code path} into tokens, and
832 * this method recursively traverses the path starting from the root
833 * (rather than starting from this node). The traversal is otherwise
834 * identical to the one described for relative path names. Dropping
835 * the lock on this node prior to commencing the traversal at the root
836 * node is essential to avoid the possibility of deadlock, as per the
837 * {@link #lock locking invariant}.
838 *
839 * @param path the path name of the preference node to return.
840 * @return the specified preference node.
841 * @throws IllegalArgumentException if the path name is invalid (i.e.,
842 * it contains multiple consecutive slash characters, or ends
843 * with a slash character and is more than one character long).
844 * @throws IllegalStateException if this node (or an ancestor) has been
845 * removed with the {@link #removeNode()} method.
846 */
847 public Preferences node(String path) {
848 synchronized(lock) {
849 if (removed)
850 throw new IllegalStateException("Node has been removed.");
851 if (path.equals(""))
852 return this;
853 if (path.equals("/"))
854 return root;
855 if (path.charAt(0) != '/')
856 return node(new StringTokenizer(path, "/", true));
857 }
858
859 // Absolute path. Note that we've dropped our lock to avoid deadlock
860 return root.node(new StringTokenizer(path.substring(1), "/", true));
861 }
862
863 /**
864 * tokenizer contains <name> {'/' <name>}*
865 */
866 private Preferences node(StringTokenizer path) {
867 String token = path.nextToken();
868 if (token.equals("/")) // Check for consecutive slashes
869 throw new IllegalArgumentException("Consecutive slashes in path");
870 synchronized(lock) {
871 AbstractPreferences child = kidCache.get(token);
894 * <p>This implementation is very similar to {@link #node(String)},
895 * except that {@link #getChild(String)} is used instead of {@link
896 * #childSpi(String)}.
897 *
898 * @param path the path name of the node whose existence is to be checked.
899 * @return true if the specified node exists.
900 * @throws BackingStoreException if this operation cannot be completed
901 * due to a failure in the backing store, or inability to
902 * communicate with it.
903 * @throws IllegalArgumentException if the path name is invalid (i.e.,
904 * it contains multiple consecutive slash characters, or ends
905 * with a slash character and is more than one character long).
906 * @throws IllegalStateException if this node (or an ancestor) has been
907 * removed with the {@link #removeNode()} method and
908 * {@code pathname} is not the empty string ({@code ""}).
909 */
910 public boolean nodeExists(String path)
911 throws BackingStoreException
912 {
913 synchronized(lock) {
914 if (path.equals(""))
915 return !removed;
916 if (removed)
917 throw new IllegalStateException("Node has been removed.");
918 if (path.equals("/"))
919 return true;
920 if (path.charAt(0) != '/')
921 return nodeExists(new StringTokenizer(path, "/", true));
922 }
923
924 // Absolute path. Note that we've dropped our lock to avoid deadlock
925 return root.nodeExists(new StringTokenizer(path.substring(1), "/",
926 true));
927 }
928
929 /**
930 * tokenizer contains <name> {'/' <name>}*
931 */
932 private boolean nodeExists(StringTokenizer path)
933 throws BackingStoreException
934 {
|
186 * intentional or unintentional denial of service due to a locked node.
187 * To avoid deadlock, a node is <i>never</i> locked by a thread that
188 * holds a lock on a descendant of that node.
189 */
190 protected final Object lock = new Object();
191
192 /**
193 * Creates a preference node with the specified parent and the specified
194 * name relative to its parent.
195 *
196 * @param parent the parent of this preference node, or null if this
197 * is the root.
198 * @param name the name of this preference node, relative to its parent,
199 * or {@code ""} if this is the root.
200 * @throws IllegalArgumentException if {@code name} contains a slash
201 * ({@code '/'}), or {@code parent} is {@code null} and
202 * name isn't {@code ""}.
203 */
204 protected AbstractPreferences(AbstractPreferences parent, String name) {
205 if (parent==null) {
206 if (!name.isEmpty())
207 throw new IllegalArgumentException("Root name '"+name+
208 "' must be \"\"");
209 this.absolutePath = "/";
210 root = this;
211 } else {
212 if (name.indexOf('/') != -1)
213 throw new IllegalArgumentException("Name '" + name +
214 "' contains '/'");
215 if (name.isEmpty())
216 throw new IllegalArgumentException("Illegal name: empty string");
217
218 root = parent.root;
219 absolutePath = (parent==root ? "/" + name
220 : parent.absolutePath() + "/" + name);
221 }
222 this.name = name;
223 this.parent = parent;
224 }
225
226 /**
227 * Implements the {@code put} method as per the specification in
228 * {@link Preferences#put(String,String)}.
229 *
230 * <p>This implementation checks that the key and value are legal,
231 * obtains this preference node's lock, checks that the node
232 * has not been removed, invokes {@link #putSpi(String,String)}, and if
233 * there are any preference change listeners, enqueues a notification
234 * event for processing by the event dispatch thread.
235 *
831 * lock is dropped prior to breaking {@code path} into tokens, and
832 * this method recursively traverses the path starting from the root
833 * (rather than starting from this node). The traversal is otherwise
834 * identical to the one described for relative path names. Dropping
835 * the lock on this node prior to commencing the traversal at the root
836 * node is essential to avoid the possibility of deadlock, as per the
837 * {@link #lock locking invariant}.
838 *
839 * @param path the path name of the preference node to return.
840 * @return the specified preference node.
841 * @throws IllegalArgumentException if the path name is invalid (i.e.,
842 * it contains multiple consecutive slash characters, or ends
843 * with a slash character and is more than one character long).
844 * @throws IllegalStateException if this node (or an ancestor) has been
845 * removed with the {@link #removeNode()} method.
846 */
847 public Preferences node(String path) {
848 synchronized(lock) {
849 if (removed)
850 throw new IllegalStateException("Node has been removed.");
851 if (path.isEmpty())
852 return this;
853 if (path.equals("/"))
854 return root;
855 if (path.charAt(0) != '/')
856 return node(new StringTokenizer(path, "/", true));
857 }
858
859 // Absolute path. Note that we've dropped our lock to avoid deadlock
860 return root.node(new StringTokenizer(path.substring(1), "/", true));
861 }
862
863 /**
864 * tokenizer contains <name> {'/' <name>}*
865 */
866 private Preferences node(StringTokenizer path) {
867 String token = path.nextToken();
868 if (token.equals("/")) // Check for consecutive slashes
869 throw new IllegalArgumentException("Consecutive slashes in path");
870 synchronized(lock) {
871 AbstractPreferences child = kidCache.get(token);
894 * <p>This implementation is very similar to {@link #node(String)},
895 * except that {@link #getChild(String)} is used instead of {@link
896 * #childSpi(String)}.
897 *
898 * @param path the path name of the node whose existence is to be checked.
899 * @return true if the specified node exists.
900 * @throws BackingStoreException if this operation cannot be completed
901 * due to a failure in the backing store, or inability to
902 * communicate with it.
903 * @throws IllegalArgumentException if the path name is invalid (i.e.,
904 * it contains multiple consecutive slash characters, or ends
905 * with a slash character and is more than one character long).
906 * @throws IllegalStateException if this node (or an ancestor) has been
907 * removed with the {@link #removeNode()} method and
908 * {@code pathname} is not the empty string ({@code ""}).
909 */
910 public boolean nodeExists(String path)
911 throws BackingStoreException
912 {
913 synchronized(lock) {
914 if (path.isEmpty())
915 return !removed;
916 if (removed)
917 throw new IllegalStateException("Node has been removed.");
918 if (path.equals("/"))
919 return true;
920 if (path.charAt(0) != '/')
921 return nodeExists(new StringTokenizer(path, "/", true));
922 }
923
924 // Absolute path. Note that we've dropped our lock to avoid deadlock
925 return root.nodeExists(new StringTokenizer(path.substring(1), "/",
926 true));
927 }
928
929 /**
930 * tokenizer contains <name> {'/' <name>}*
931 */
932 private boolean nodeExists(StringTokenizer path)
933 throws BackingStoreException
934 {
|