FileSystemPreferences.java revision 28
1472N/A * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. 0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 0N/A * This code is free software; you can redistribute it and/or modify it 0N/A * under the terms of the GNU General Public License version 2 only, as 0N/A * published by the Free Software Foundation. Sun designates this 0N/A * particular file as subject to the "Classpath" exception as provided 0N/A * by Sun in the LICENSE file that accompanied this code. 0N/A * This code is distributed in the hope that it will be useful, but WITHOUT 0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 0N/A * version 2 for more details (a copy is included in the LICENSE file that 0N/A * accompanied this code). 0N/A * You should have received a copy of the GNU General Public License version 0N/A * 2 along with this work; if not, write to the Free Software Foundation, 1472N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1472N/A * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 0N/A * CA 95054 USA or visit www.sun.com if you need additional information or 0N/A * have any questions. 0N/A * Preferences implementation for Unix. Preferences are stored in the file 0N/A * system, with one directory per preferences node. All of the preferences 0N/A * at each node are stored in a single file. Atomic file system operations 0N/A * (e.g. File.renameTo) are used to ensure integrity. An in-memory cache of 0N/A * the "explored" portion of the tree is maintained for performance, and 0N/A * written back to the disk periodically. File-locking is used to ensure 0N/A * reasonable behavior when multiple VMs are running at the same time. 0N/A * (The file lock is obtained only for sync(), flush() and removeNode().) 0N/A * @author Josh Bloch 642N/A * Sync interval in seconds. 0N/A "java.util.prefs.syncInterval",
"30"))));
0N/A * Returns logger for error messages. Backing store exceptions are logged at 0N/A * Directory for system preferences. 0N/A * Flag, indicating whether systemRoot directory is writable 0N/A * Directory for user preferences. 0N/A * Flag, indicating whether userRoot directory is writable 0N/A // Attempt to create root dir if it does not yet exist. 0N/A " on userRoot directory. ");
0N/A " directory. User preferences are unusable.");
0N/A // create if does not exist. 0N/A "mod file. Chmod failed on " +
642N/A // Attempt to create root dir if it does not yet exist. 642N/A // system root does not exist in /etc/.java 642N/A // Switching to java.home 0N/A "Created system preferences directory " 0N/A +
"system preferences directory. System " 0N/A +
"preferences are unusable.");
0N/A // create if does not exist. 0N/A * The lock file for the user tree. 0N/A * The lock file for the system tree. 0N/A * Unix lock handle for userRoot. 0N/A * Zero, if unlocked. 0N/A * Unix lock handle for systemRoot. 0N/A * Zero, if unlocked. 0N/A * The directory representing this preference node. There is no guarantee 0N/A * that this directory exits, as another VM can delete it at any time 0N/A * that it (the other VM) holds the file-lock. While the root node cannot 0N/A * be deleted, it may not yet have been created, or the underlying 0N/A * directory could have been deleted accidentally. 0N/A * The file representing this preference node's preferences. 0N/A * The file format is undocumented, and subject to change 0N/A * from release to release, but I'm sure that you can figure 0N/A * it out if you try real hard. 0N/A * A temporary file used for saving changes to preferences. As part of 0N/A * the sync operation, changes are first saved into this file, and then 0N/A * atomically renamed to prefsFile. This results in an atomic state 0N/A * change from one valid set of preferences to another. The 0N/A * the file-lock is held for the duration of this transformation. 0N/A * File, which keeps track of global modifications of userRoot. 0N/A * Flag, which indicated whether userRoot was modified by another VM 0N/A * Keeps track of userRoot modification time. This time is reset to 0N/A * zero after UNIX reboot, and is increased by 1 second each time 0N/A * userRoot is modified. 0N/A * File, which keeps track of global modifications of systemRoot 0N/A * Flag, which indicates whether systemRoot was modified by another VM 0N/A * Keeps track of systemRoot modification time. This time is reset to 0N/A * zero after system reboot, and is increased by 1 second each time 0N/A * systemRoot is modified. 642N/A * Locally cached preferences for this node (includes uncommitted 0N/A * changes). This map is initialized with from disk when the first get or 0N/A * put operation occurs on this node. It is synchronized with the 0N/A * corresponding disk file (prefsFile) by the sync operation. The initial 113N/A * value is read *without* acquiring the file-lock. 113N/A * The last modification time of the file backing this node at the time 113N/A * that prefCache was last synchronized (or initially read). This 642N/A * value is set *before* reading the file, so it's conservative; the 113N/A * actual timestamp could be (slightly) higher. A value of zero indicates 113N/A * that we were unable to initialize prefsCache from the disk, or 113N/A * have not yet attempted to do so. (If prefsCache is non-null, it 113N/A * indicates the former; if it's null, the latter.) 642N/A * Unix error code for locked file. 0N/A * Unix error code for denied access. 0N/A /* Used to interpret results of native functions */ 0N/A * A list of all uncommitted preference changes. The elements in this 0N/A * list are of type PrefChange. If this node is concurrently modified on 0N/A * disk by another VM, the two sets of changes are merged when this node 0N/A * is sync'ed by overwriting our prefsCache with the preference map last 0N/A * written out to disk (by the other VM), and then replaying this change 0N/A * log against that map. The resulting map is then written back 0N/A * Represents a change to a preference. 0N/A * Reapplies the change to prefsCache. 0N/A * Represents a preference put. 0N/A * Represents a preference remove. 2062N/A * Represents the creation of this node. 2062N/A * Performs no action, but the presence of this object in changeLog 0N/A * will force the node and its ancestors to be made permanent at the 0N/A * NodeCreate object for this node. 0N/A * Replay changeLog against prefsCache. 0N/A // Add periodic timer task to periodically sync cached prefs 0N/A // Add shutdown hook to flush cached prefs on normal termination * Synchronization necessary because userRoot and systemRoot are * Special constructor for roots (both user and system). This constructor * will only be called twice, by the static initializer. * Construct a new FileSystemPreferences instance with the specified * parent node and name. This constructor, called from childSpi, * is used to make every node except for the two //roots. // These 2 things guarantee node will get wrtten at next flush/sync * Initialize prefsCache if it has yet to be initialized. When this method * returns, prefsCache will be non-null. If the data was successfully * read from the file, lastSyncTime will be updated. If prefsCache was * null, but it was impossible to read the file (because it didn't * exist or for any other reason) prefsCache will be initialized to an * empty, modifiable Map, and lastSyncTime remain zero. // assert lastSyncTime == 0; * Attempt to load prefsCache from the backing store. If the attempt * succeeds, lastSyncTime will be updated (the new value will typically * correspond to the data loaded into the map, but it may be less, * if another VM is updating this node concurrently). If the attempt * fails, a BackingStoreException is thrown and both prefsCache and * lastSyncTime are unaffected by the call. // Attempt succeeded; update state * Attempt to write back prefsCache to the backing store. If the attempt * succeeds, lastSyncTime will be updated (the new value will correspond * exactly to the data thust written back, as we hold the file lock, which * prevents a concurrent write. If the attempt fails, a * BackingStoreException is thrown and both the backing store (prefsFile) * and lastSyncTime will be unaffected by this call. This call will * NEVER leave prefsFile in a corrupt state. // to remove a node we need an exclusive lock * Called with file lock held (in addition to node locks). // dir should be empty now. If it's not, empty it "Found extraneous files when removing node: " shared =
false;
/* use exclusive lock for user prefs */ /* if can write to system root, use exclusive lock. otherwise use shared lock. */ return;
// We've never been used, don't bother syncing // Prefs at this node were externally modified; read in node and // playback any local mods since last sync // This node was removed in the background. Playback any changes // against a virgin (empty) Map. * Attempt succeeded; it's barely possible that the call to * lastModified might fail (i.e., return 0), but this would not * be a disaster, as lastSyncTime is allowed to lag. /* If lastSyncTime did not change, or went back * increment by 1 second. Since we hold the lock * lastSyncTime always monotonically encreases in the * Returns true if the specified character is appropriate for use in * Unix directory names. A character is appropriate if it's a printable * ASCII character (> 0x1f && < 0x7f) and unequal to slash ('/', 0x2f), * dot ('.', 0x2e), or underscore ('_', 0x5f). return ch >
0x1f &&
ch <
0x7f &&
ch !=
'/' &&
ch !=
'.' &&
ch !=
'_';
* Returns the directory name corresponding to the specified node name. * Generally, this is just the node name. If the node name includes * inappropriate characters (as per isDirChar) it is translated to Base64. * with the underscore character ('_', 0x5f) prepended. * Translate a string into a byte array by translating each character * into two bytes, high-byte first ("big-endian"). for (
int i=
0, j=
0; i<
len; i++) {
* Returns the node name corresponding to the specified directory name. * (Inverts the transformation of dirName(String). for (
int i =
0; i < a.
length; ) {
* Try to acquire the appropriate file lock (user or system). If * the initial attempt fails, several more attempts are made using * an exponential backoff strategy. If all attempts fail, this method * @throws SecurityException if file access denied. // // If at first, you don't succeed... * Checks if unlockFile0() returned an error. Throws a SecurityException, * if access denied. Logs a warning otherwise. " Lock file access denied.");
(
isUserNode()?
"User prefs. " :
"System prefs.") +
* Locks file using UNIX file locking. * @param fileName Absolute file name of the lock file. * @return Returns a lock handle, used to unlock the file. private static native int[]
* Unlocks file previously locked by lockFile0(). * @param lockHandle Handle to the file lock. * @return Returns zero if OK, UNIX error code if failure. * Changes UNIX file permissions. * Initial time between lock attempts, in ms. The time is doubled * after each failing attempt (except the first). * Maximum number of lock attempts. * Release the the appropriate file lock (user or system). * @throws SecurityException if file access denied. (
usernode ?
"user":
"system") +
" preferences.)");
(
isUserNode() ?
"user" :
"system") +
" preferences." +
" Unix error code " +
result +
".");
" Lock file access denied.");