author | herrick |
Fri, 19 Apr 2019 07:55:28 -0400 | |
branch | JDK-8200758-branch |
changeset 57331 | 221a589c52ee |
parent 47216 | 71c04702a3d5 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
35311
613162418a3d
8147545: Remove sun.misc.ManagedLocalsThread from java.prefs
bpb
parents:
30043
diff
changeset
|
2 |
* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. |
2 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 10 |
* |
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
5506 | 21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
2 | 24 |
*/ |
25 |
||
26 |
package java.util.prefs; |
|
27 |
import java.util.*; |
|
28 |
import java.io.*; |
|
29 |
import java.security.AccessController; |
|
30 |
import java.security.PrivilegedAction; |
|
31 |
import java.security.PrivilegedExceptionAction; |
|
32 |
import java.security.PrivilegedActionException; |
|
4230
e208dda74f1c
6899607: Update java.util.prefs.FileSystemPreferences to use PlatformLogger
mchung
parents:
715
diff
changeset
|
33 |
import sun.util.logging.PlatformLogger; |
2 | 34 |
|
35 |
/** |
|
36 |
* Preferences implementation for Unix. Preferences are stored in the file |
|
37 |
* system, with one directory per preferences node. All of the preferences |
|
38 |
* at each node are stored in a single file. Atomic file system operations |
|
39 |
* (e.g. File.renameTo) are used to ensure integrity. An in-memory cache of |
|
40 |
* the "explored" portion of the tree is maintained for performance, and |
|
41 |
* written back to the disk periodically. File-locking is used to ensure |
|
42 |
* reasonable behavior when multiple VMs are running at the same time. |
|
43 |
* (The file lock is obtained only for sync(), flush() and removeNode().) |
|
44 |
* |
|
45 |
* @author Josh Bloch |
|
46 |
* @see Preferences |
|
47 |
* @since 1.4 |
|
48 |
*/ |
|
49 |
class FileSystemPreferences extends AbstractPreferences { |
|
22951
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
50 |
|
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
51 |
static { |
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
52 |
PrivilegedAction<Void> load = () -> { |
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
53 |
System.loadLibrary("prefs"); |
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
54 |
return null; |
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
55 |
}; |
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
56 |
AccessController.doPrivileged(load); |
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
57 |
} |
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
58 |
|
2 | 59 |
/** |
60 |
* Sync interval in seconds. |
|
61 |
*/ |
|
62 |
private static final int SYNC_INTERVAL = Math.max(1, |
|
23582
d5fa3327ab3a
8038177: Eliminate unnecessary dependency to sun.security.action
mchung
parents:
22951
diff
changeset
|
63 |
AccessController.doPrivileged((PrivilegedAction<Integer>) () -> |
d5fa3327ab3a
8038177: Eliminate unnecessary dependency to sun.security.action
mchung
parents:
22951
diff
changeset
|
64 |
Integer.getInteger("java.util.prefs.syncInterval", 30))); |
2 | 65 |
|
66 |
/** |
|
67 |
* Returns logger for error messages. Backing store exceptions are logged at |
|
68 |
* WARNING level. |
|
69 |
*/ |
|
4230
e208dda74f1c
6899607: Update java.util.prefs.FileSystemPreferences to use PlatformLogger
mchung
parents:
715
diff
changeset
|
70 |
private static PlatformLogger getLogger() { |
e208dda74f1c
6899607: Update java.util.prefs.FileSystemPreferences to use PlatformLogger
mchung
parents:
715
diff
changeset
|
71 |
return PlatformLogger.getLogger("java.util.prefs"); |
2 | 72 |
} |
73 |
||
74 |
/** |
|
75 |
* Directory for system preferences. |
|
76 |
*/ |
|
77 |
private static File systemRootDir; |
|
78 |
||
79 |
/* |
|
80 |
* Flag, indicating whether systemRoot directory is writable |
|
81 |
*/ |
|
82 |
private static boolean isSystemRootWritable; |
|
83 |
||
84 |
/** |
|
85 |
* Directory for user preferences. |
|
86 |
*/ |
|
87 |
private static File userRootDir; |
|
88 |
||
89 |
/* |
|
90 |
* Flag, indicating whether userRoot directory is writable |
|
91 |
*/ |
|
92 |
private static boolean isUserRootWritable; |
|
93 |
||
94 |
/** |
|
95 |
* The user root. |
|
96 |
*/ |
|
38887
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
97 |
private static volatile Preferences userRoot; |
2 | 98 |
|
38887
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
99 |
static Preferences getUserRoot() { |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
100 |
Preferences root = userRoot; |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
101 |
if (root == null) { |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
102 |
synchronized (FileSystemPreferences.class) { |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
103 |
root = userRoot; |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
104 |
if (root == null) { |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
105 |
setupUserRoot(); |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
106 |
userRoot = root = new FileSystemPreferences(true); |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
107 |
} |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
108 |
} |
2 | 109 |
} |
38887
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
110 |
return root; |
2 | 111 |
} |
112 |
||
113 |
private static void setupUserRoot() { |
|
51 | 114 |
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
115 |
public Void run() { |
|
2 | 116 |
userRootDir = |
117 |
new File(System.getProperty("java.util.prefs.userRoot", |
|
118 |
System.getProperty("user.home")), ".java/.userPrefs"); |
|
119 |
// Attempt to create root dir if it does not yet exist. |
|
120 |
if (!userRootDir.exists()) { |
|
121 |
if (userRootDir.mkdirs()) { |
|
122 |
try { |
|
123 |
chmod(userRootDir.getCanonicalPath(), USER_RWX); |
|
124 |
} catch (IOException e) { |
|
125 |
getLogger().warning("Could not change permissions" + |
|
126 |
" on userRoot directory. "); |
|
127 |
} |
|
128 |
getLogger().info("Created user preferences directory."); |
|
129 |
} |
|
130 |
else |
|
131 |
getLogger().warning("Couldn't create user preferences" + |
|
132 |
" directory. User preferences are unusable."); |
|
133 |
} |
|
134 |
isUserRootWritable = userRootDir.canWrite(); |
|
135 |
String USER_NAME = System.getProperty("user.name"); |
|
136 |
userLockFile = new File (userRootDir,".user.lock." + USER_NAME); |
|
137 |
userRootModFile = new File (userRootDir, |
|
138 |
".userRootModFile." + USER_NAME); |
|
139 |
if (!userRootModFile.exists()) |
|
140 |
try { |
|
141 |
// create if does not exist. |
|
142 |
userRootModFile.createNewFile(); |
|
143 |
// Only user can read/write userRootModFile. |
|
144 |
int result = chmod(userRootModFile.getCanonicalPath(), |
|
145 |
USER_READ_WRITE); |
|
146 |
if (result !=0) |
|
147 |
getLogger().warning("Problem creating userRoot " + |
|
148 |
"mod file. Chmod failed on " + |
|
149 |
userRootModFile.getCanonicalPath() + |
|
150 |
" Unix error code " + result); |
|
151 |
} catch (IOException e) { |
|
152 |
getLogger().warning(e.toString()); |
|
153 |
} |
|
154 |
userRootModTime = userRootModFile.lastModified(); |
|
155 |
return null; |
|
156 |
} |
|
157 |
}); |
|
158 |
} |
|
159 |
||
160 |
||
161 |
/** |
|
162 |
* The system root. |
|
163 |
*/ |
|
38887
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
164 |
private static volatile Preferences systemRoot; |
2 | 165 |
|
38887
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
166 |
static Preferences getSystemRoot() { |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
167 |
Preferences root = systemRoot; |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
168 |
if (root == null) { |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
169 |
synchronized (FileSystemPreferences.class) { |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
170 |
root = systemRoot; |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
171 |
if (root == null) { |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
172 |
setupSystemRoot(); |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
173 |
systemRoot = root = new FileSystemPreferences(false); |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
174 |
} |
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
175 |
} |
2 | 176 |
} |
38887
bb8ffdf4e7aa
8139507: WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs
jlahoda
parents:
35311
diff
changeset
|
177 |
return root; |
2 | 178 |
} |
179 |
||
180 |
private static void setupSystemRoot() { |
|
51 | 181 |
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
182 |
public Void run() { |
|
183 |
String systemPrefsDirName = |
|
2 | 184 |
System.getProperty("java.util.prefs.systemRoot","/etc/.java"); |
185 |
systemRootDir = |
|
186 |
new File(systemPrefsDirName, ".systemPrefs"); |
|
187 |
// Attempt to create root dir if it does not yet exist. |
|
188 |
if (!systemRootDir.exists()) { |
|
189 |
// system root does not exist in /etc/.java |
|
190 |
// Switching to java.home |
|
191 |
systemRootDir = |
|
192 |
new File(System.getProperty("java.home"), |
|
193 |
".systemPrefs"); |
|
194 |
if (!systemRootDir.exists()) { |
|
195 |
if (systemRootDir.mkdirs()) { |
|
196 |
getLogger().info( |
|
197 |
"Created system preferences directory " |
|
198 |
+ "in java.home."); |
|
199 |
try { |
|
200 |
chmod(systemRootDir.getCanonicalPath(), |
|
201 |
USER_RWX_ALL_RX); |
|
202 |
} catch (IOException e) { |
|
203 |
} |
|
204 |
} else { |
|
205 |
getLogger().warning("Could not create " |
|
206 |
+ "system preferences directory. System " |
|
207 |
+ "preferences are unusable."); |
|
208 |
} |
|
209 |
} |
|
210 |
} |
|
211 |
isSystemRootWritable = systemRootDir.canWrite(); |
|
212 |
systemLockFile = new File(systemRootDir, ".system.lock"); |
|
213 |
systemRootModFile = |
|
214 |
new File (systemRootDir,".systemRootModFile"); |
|
215 |
if (!systemRootModFile.exists() && isSystemRootWritable) |
|
216 |
try { |
|
217 |
// create if does not exist. |
|
218 |
systemRootModFile.createNewFile(); |
|
219 |
int result = chmod(systemRootModFile.getCanonicalPath(), |
|
220 |
USER_RW_ALL_READ); |
|
221 |
if (result !=0) |
|
222 |
getLogger().warning("Chmod failed on " + |
|
223 |
systemRootModFile.getCanonicalPath() + |
|
224 |
" Unix error code " + result); |
|
225 |
} catch (IOException e) { getLogger().warning(e.toString()); |
|
226 |
} |
|
227 |
systemRootModTime = systemRootModFile.lastModified(); |
|
228 |
return null; |
|
229 |
} |
|
230 |
}); |
|
231 |
} |
|
232 |
||
233 |
||
234 |
/** |
|
235 |
* Unix user write/read permission |
|
236 |
*/ |
|
237 |
private static final int USER_READ_WRITE = 0600; |
|
238 |
||
239 |
private static final int USER_RW_ALL_READ = 0644; |
|
240 |
||
241 |
||
242 |
private static final int USER_RWX_ALL_RX = 0755; |
|
243 |
||
244 |
private static final int USER_RWX = 0700; |
|
245 |
||
246 |
/** |
|
247 |
* The lock file for the user tree. |
|
248 |
*/ |
|
249 |
static File userLockFile; |
|
250 |
||
251 |
||
252 |
||
253 |
/** |
|
254 |
* The lock file for the system tree. |
|
255 |
*/ |
|
256 |
static File systemLockFile; |
|
257 |
||
258 |
/** |
|
259 |
* Unix lock handle for userRoot. |
|
260 |
* Zero, if unlocked. |
|
261 |
*/ |
|
262 |
||
263 |
private static int userRootLockHandle = 0; |
|
264 |
||
265 |
/** |
|
266 |
* Unix lock handle for systemRoot. |
|
267 |
* Zero, if unlocked. |
|
268 |
*/ |
|
269 |
||
270 |
private static int systemRootLockHandle = 0; |
|
271 |
||
272 |
/** |
|
273 |
* The directory representing this preference node. There is no guarantee |
|
274 |
* that this directory exits, as another VM can delete it at any time |
|
275 |
* that it (the other VM) holds the file-lock. While the root node cannot |
|
276 |
* be deleted, it may not yet have been created, or the underlying |
|
277 |
* directory could have been deleted accidentally. |
|
278 |
*/ |
|
279 |
private final File dir; |
|
280 |
||
281 |
/** |
|
282 |
* The file representing this preference node's preferences. |
|
283 |
* The file format is undocumented, and subject to change |
|
284 |
* from release to release, but I'm sure that you can figure |
|
285 |
* it out if you try real hard. |
|
286 |
*/ |
|
287 |
private final File prefsFile; |
|
288 |
||
289 |
/** |
|
290 |
* A temporary file used for saving changes to preferences. As part of |
|
291 |
* the sync operation, changes are first saved into this file, and then |
|
292 |
* atomically renamed to prefsFile. This results in an atomic state |
|
293 |
* change from one valid set of preferences to another. The |
|
294 |
* the file-lock is held for the duration of this transformation. |
|
295 |
*/ |
|
296 |
private final File tmpFile; |
|
297 |
||
298 |
/** |
|
299 |
* File, which keeps track of global modifications of userRoot. |
|
300 |
*/ |
|
301 |
private static File userRootModFile; |
|
302 |
||
303 |
/** |
|
304 |
* Flag, which indicated whether userRoot was modified by another VM |
|
305 |
*/ |
|
306 |
private static boolean isUserRootModified = false; |
|
307 |
||
308 |
/** |
|
309 |
* Keeps track of userRoot modification time. This time is reset to |
|
310 |
* zero after UNIX reboot, and is increased by 1 second each time |
|
311 |
* userRoot is modified. |
|
312 |
*/ |
|
313 |
private static long userRootModTime; |
|
314 |
||
315 |
||
316 |
/* |
|
317 |
* File, which keeps track of global modifications of systemRoot |
|
318 |
*/ |
|
319 |
private static File systemRootModFile; |
|
320 |
/* |
|
321 |
* Flag, which indicates whether systemRoot was modified by another VM |
|
322 |
*/ |
|
323 |
private static boolean isSystemRootModified = false; |
|
324 |
||
325 |
/** |
|
326 |
* Keeps track of systemRoot modification time. This time is reset to |
|
327 |
* zero after system reboot, and is increased by 1 second each time |
|
328 |
* systemRoot is modified. |
|
329 |
*/ |
|
330 |
private static long systemRootModTime; |
|
331 |
||
332 |
/** |
|
333 |
* Locally cached preferences for this node (includes uncommitted |
|
334 |
* changes). This map is initialized with from disk when the first get or |
|
335 |
* put operation occurs on this node. It is synchronized with the |
|
336 |
* corresponding disk file (prefsFile) by the sync operation. The initial |
|
337 |
* value is read *without* acquiring the file-lock. |
|
338 |
*/ |
|
51 | 339 |
private Map<String, String> prefsCache = null; |
2 | 340 |
|
341 |
/** |
|
342 |
* The last modification time of the file backing this node at the time |
|
343 |
* that prefCache was last synchronized (or initially read). This |
|
344 |
* value is set *before* reading the file, so it's conservative; the |
|
345 |
* actual timestamp could be (slightly) higher. A value of zero indicates |
|
346 |
* that we were unable to initialize prefsCache from the disk, or |
|
347 |
* have not yet attempted to do so. (If prefsCache is non-null, it |
|
348 |
* indicates the former; if it's null, the latter.) |
|
349 |
*/ |
|
350 |
private long lastSyncTime = 0; |
|
351 |
||
352 |
/** |
|
353 |
* Unix error code for locked file. |
|
354 |
*/ |
|
355 |
private static final int EAGAIN = 11; |
|
356 |
||
357 |
/** |
|
358 |
* Unix error code for denied access. |
|
359 |
*/ |
|
360 |
private static final int EACCES = 13; |
|
361 |
||
362 |
/* Used to interpret results of native functions */ |
|
363 |
private static final int LOCK_HANDLE = 0; |
|
364 |
private static final int ERROR_CODE = 1; |
|
365 |
||
366 |
/** |
|
367 |
* A list of all uncommitted preference changes. The elements in this |
|
368 |
* list are of type PrefChange. If this node is concurrently modified on |
|
369 |
* disk by another VM, the two sets of changes are merged when this node |
|
370 |
* is sync'ed by overwriting our prefsCache with the preference map last |
|
371 |
* written out to disk (by the other VM), and then replaying this change |
|
372 |
* log against that map. The resulting map is then written back |
|
373 |
* to the disk. |
|
374 |
*/ |
|
7803
56bc97d69d93
6880112: Project Coin: Port JDK core library code to use diamond operator
smarks
parents:
5506
diff
changeset
|
375 |
final List<Change> changeLog = new ArrayList<>(); |
2 | 376 |
|
377 |
/** |
|
378 |
* Represents a change to a preference. |
|
379 |
*/ |
|
380 |
private abstract class Change { |
|
381 |
/** |
|
382 |
* Reapplies the change to prefsCache. |
|
383 |
*/ |
|
384 |
abstract void replay(); |
|
385 |
}; |
|
386 |
||
387 |
/** |
|
388 |
* Represents a preference put. |
|
389 |
*/ |
|
390 |
private class Put extends Change { |
|
391 |
String key, value; |
|
392 |
||
393 |
Put(String key, String value) { |
|
394 |
this.key = key; |
|
395 |
this.value = value; |
|
396 |
} |
|
397 |
||
398 |
void replay() { |
|
399 |
prefsCache.put(key, value); |
|
400 |
} |
|
401 |
} |
|
402 |
||
403 |
/** |
|
404 |
* Represents a preference remove. |
|
405 |
*/ |
|
406 |
private class Remove extends Change { |
|
407 |
String key; |
|
408 |
||
409 |
Remove(String key) { |
|
410 |
this.key = key; |
|
411 |
} |
|
412 |
||
413 |
void replay() { |
|
414 |
prefsCache.remove(key); |
|
415 |
} |
|
416 |
} |
|
417 |
||
418 |
/** |
|
419 |
* Represents the creation of this node. |
|
420 |
*/ |
|
421 |
private class NodeCreate extends Change { |
|
422 |
/** |
|
423 |
* Performs no action, but the presence of this object in changeLog |
|
424 |
* will force the node and its ancestors to be made permanent at the |
|
425 |
* next sync. |
|
426 |
*/ |
|
427 |
void replay() { |
|
428 |
} |
|
429 |
} |
|
430 |
||
431 |
/** |
|
432 |
* NodeCreate object for this node. |
|
433 |
*/ |
|
434 |
NodeCreate nodeCreate = null; |
|
435 |
||
436 |
/** |
|
437 |
* Replay changeLog against prefsCache. |
|
438 |
*/ |
|
439 |
private void replayChanges() { |
|
440 |
for (int i = 0, n = changeLog.size(); i<n; i++) |
|
51 | 441 |
changeLog.get(i).replay(); |
2 | 442 |
} |
443 |
||
444 |
private static Timer syncTimer = new Timer(true); // Daemon Thread |
|
445 |
||
446 |
static { |
|
447 |
// Add periodic timer task to periodically sync cached prefs |
|
448 |
syncTimer.schedule(new TimerTask() { |
|
449 |
public void run() { |
|
450 |
syncWorld(); |
|
451 |
} |
|
452 |
}, SYNC_INTERVAL*1000, SYNC_INTERVAL*1000); |
|
453 |
||
454 |
// Add shutdown hook to flush cached prefs on normal termination |
|
51 | 455 |
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
456 |
public Void run() { |
|
35311
613162418a3d
8147545: Remove sun.misc.ManagedLocalsThread from java.prefs
bpb
parents:
30043
diff
changeset
|
457 |
Runtime.getRuntime().addShutdownHook( |
613162418a3d
8147545: Remove sun.misc.ManagedLocalsThread from java.prefs
bpb
parents:
30043
diff
changeset
|
458 |
new Thread(null, null, "Sync Timer Thread", 0, false) { |
2 | 459 |
public void run() { |
460 |
syncTimer.cancel(); |
|
461 |
syncWorld(); |
|
462 |
} |
|
463 |
}); |
|
464 |
return null; |
|
465 |
} |
|
466 |
}); |
|
467 |
} |
|
468 |
||
469 |
private static void syncWorld() { |
|
470 |
/* |
|
471 |
* Synchronization necessary because userRoot and systemRoot are |
|
472 |
* lazily initialized. |
|
473 |
*/ |
|
474 |
Preferences userRt; |
|
475 |
Preferences systemRt; |
|
476 |
synchronized(FileSystemPreferences.class) { |
|
477 |
userRt = userRoot; |
|
478 |
systemRt = systemRoot; |
|
479 |
} |
|
480 |
||
481 |
try { |
|
482 |
if (userRt != null) |
|
483 |
userRt.flush(); |
|
484 |
} catch(BackingStoreException e) { |
|
485 |
getLogger().warning("Couldn't flush user prefs: " + e); |
|
486 |
} |
|
487 |
||
488 |
try { |
|
489 |
if (systemRt != null) |
|
490 |
systemRt.flush(); |
|
491 |
} catch(BackingStoreException e) { |
|
492 |
getLogger().warning("Couldn't flush system prefs: " + e); |
|
493 |
} |
|
494 |
} |
|
495 |
||
496 |
private final boolean isUserNode; |
|
497 |
||
498 |
/** |
|
499 |
* Special constructor for roots (both user and system). This constructor |
|
500 |
* will only be called twice, by the static initializer. |
|
501 |
*/ |
|
502 |
private FileSystemPreferences(boolean user) { |
|
503 |
super(null, ""); |
|
504 |
isUserNode = user; |
|
505 |
dir = (user ? userRootDir: systemRootDir); |
|
506 |
prefsFile = new File(dir, "prefs.xml"); |
|
507 |
tmpFile = new File(dir, "prefs.tmp"); |
|
508 |
} |
|
509 |
||
510 |
/** |
|
511 |
* Construct a new FileSystemPreferences instance with the specified |
|
512 |
* parent node and name. This constructor, called from childSpi, |
|
513 |
* is used to make every node except for the two //roots. |
|
514 |
*/ |
|
515 |
private FileSystemPreferences(FileSystemPreferences parent, String name) { |
|
516 |
super(parent, name); |
|
517 |
isUserNode = parent.isUserNode; |
|
518 |
dir = new File(parent.dir, dirName(name)); |
|
519 |
prefsFile = new File(dir, "prefs.xml"); |
|
520 |
tmpFile = new File(dir, "prefs.tmp"); |
|
51 | 521 |
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
522 |
public Void run() { |
|
2 | 523 |
newNode = !dir.exists(); |
524 |
return null; |
|
525 |
} |
|
526 |
}); |
|
527 |
if (newNode) { |
|
528 |
// These 2 things guarantee node will get wrtten at next flush/sync |
|
7803
56bc97d69d93
6880112: Project Coin: Port JDK core library code to use diamond operator
smarks
parents:
5506
diff
changeset
|
529 |
prefsCache = new TreeMap<>(); |
2 | 530 |
nodeCreate = new NodeCreate(); |
531 |
changeLog.add(nodeCreate); |
|
532 |
} |
|
533 |
} |
|
534 |
||
535 |
public boolean isUserNode() { |
|
536 |
return isUserNode; |
|
537 |
} |
|
538 |
||
539 |
protected void putSpi(String key, String value) { |
|
540 |
initCacheIfNecessary(); |
|
541 |
changeLog.add(new Put(key, value)); |
|
542 |
prefsCache.put(key, value); |
|
543 |
} |
|
544 |
||
545 |
protected String getSpi(String key) { |
|
546 |
initCacheIfNecessary(); |
|
51 | 547 |
return prefsCache.get(key); |
2 | 548 |
} |
549 |
||
550 |
protected void removeSpi(String key) { |
|
551 |
initCacheIfNecessary(); |
|
552 |
changeLog.add(new Remove(key)); |
|
553 |
prefsCache.remove(key); |
|
554 |
} |
|
555 |
||
556 |
/** |
|
557 |
* Initialize prefsCache if it has yet to be initialized. When this method |
|
558 |
* returns, prefsCache will be non-null. If the data was successfully |
|
559 |
* read from the file, lastSyncTime will be updated. If prefsCache was |
|
560 |
* null, but it was impossible to read the file (because it didn't |
|
561 |
* exist or for any other reason) prefsCache will be initialized to an |
|
562 |
* empty, modifiable Map, and lastSyncTime remain zero. |
|
563 |
*/ |
|
564 |
private void initCacheIfNecessary() { |
|
565 |
if (prefsCache != null) |
|
566 |
return; |
|
567 |
||
568 |
try { |
|
569 |
loadCache(); |
|
570 |
} catch(Exception e) { |
|
571 |
// assert lastSyncTime == 0; |
|
7803
56bc97d69d93
6880112: Project Coin: Port JDK core library code to use diamond operator
smarks
parents:
5506
diff
changeset
|
572 |
prefsCache = new TreeMap<>(); |
2 | 573 |
} |
574 |
} |
|
575 |
||
576 |
/** |
|
577 |
* Attempt to load prefsCache from the backing store. If the attempt |
|
578 |
* succeeds, lastSyncTime will be updated (the new value will typically |
|
579 |
* correspond to the data loaded into the map, but it may be less, |
|
580 |
* if another VM is updating this node concurrently). If the attempt |
|
581 |
* fails, a BackingStoreException is thrown and both prefsCache and |
|
582 |
* lastSyncTime are unaffected by the call. |
|
583 |
*/ |
|
584 |
private void loadCache() throws BackingStoreException { |
|
585 |
try { |
|
51 | 586 |
AccessController.doPrivileged( |
587 |
new PrivilegedExceptionAction<Void>() { |
|
588 |
public Void run() throws BackingStoreException { |
|
7803
56bc97d69d93
6880112: Project Coin: Port JDK core library code to use diamond operator
smarks
parents:
5506
diff
changeset
|
589 |
Map<String, String> m = new TreeMap<>(); |
2 | 590 |
long newLastSyncTime = 0; |
591 |
try { |
|
592 |
newLastSyncTime = prefsFile.lastModified(); |
|
8543
e5ec12a932da
7021209: convert lang, math, util to use try-with-resources
smarks
parents:
7803
diff
changeset
|
593 |
try (FileInputStream fis = new FileInputStream(prefsFile)) { |
e5ec12a932da
7021209: convert lang, math, util to use try-with-resources
smarks
parents:
7803
diff
changeset
|
594 |
XmlSupport.importMap(fis, m); |
e5ec12a932da
7021209: convert lang, math, util to use try-with-resources
smarks
parents:
7803
diff
changeset
|
595 |
} |
2 | 596 |
} catch(Exception e) { |
597 |
if (e instanceof InvalidPreferencesFormatException) { |
|
598 |
getLogger().warning("Invalid preferences format in " |
|
599 |
+ prefsFile.getPath()); |
|
600 |
prefsFile.renameTo( new File( |
|
601 |
prefsFile.getParentFile(), |
|
602 |
"IncorrectFormatPrefs.xml")); |
|
7803
56bc97d69d93
6880112: Project Coin: Port JDK core library code to use diamond operator
smarks
parents:
5506
diff
changeset
|
603 |
m = new TreeMap<>(); |
2 | 604 |
} else if (e instanceof FileNotFoundException) { |
605 |
getLogger().warning("Prefs file removed in background " |
|
606 |
+ prefsFile.getPath()); |
|
607 |
} else { |
|
608 |
throw new BackingStoreException(e); |
|
609 |
} |
|
610 |
} |
|
611 |
// Attempt succeeded; update state |
|
612 |
prefsCache = m; |
|
613 |
lastSyncTime = newLastSyncTime; |
|
614 |
return null; |
|
615 |
} |
|
616 |
}); |
|
617 |
} catch (PrivilegedActionException e) { |
|
618 |
throw (BackingStoreException) e.getException(); |
|
619 |
} |
|
620 |
} |
|
621 |
||
622 |
/** |
|
623 |
* Attempt to write back prefsCache to the backing store. If the attempt |
|
624 |
* succeeds, lastSyncTime will be updated (the new value will correspond |
|
625 |
* exactly to the data thust written back, as we hold the file lock, which |
|
626 |
* prevents a concurrent write. If the attempt fails, a |
|
627 |
* BackingStoreException is thrown and both the backing store (prefsFile) |
|
628 |
* and lastSyncTime will be unaffected by this call. This call will |
|
629 |
* NEVER leave prefsFile in a corrupt state. |
|
630 |
*/ |
|
631 |
private void writeBackCache() throws BackingStoreException { |
|
632 |
try { |
|
51 | 633 |
AccessController.doPrivileged( |
634 |
new PrivilegedExceptionAction<Void>() { |
|
635 |
public Void run() throws BackingStoreException { |
|
2 | 636 |
try { |
637 |
if (!dir.exists() && !dir.mkdirs()) |
|
638 |
throw new BackingStoreException(dir + |
|
639 |
" create failed."); |
|
8543
e5ec12a932da
7021209: convert lang, math, util to use try-with-resources
smarks
parents:
7803
diff
changeset
|
640 |
try (FileOutputStream fos = new FileOutputStream(tmpFile)) { |
e5ec12a932da
7021209: convert lang, math, util to use try-with-resources
smarks
parents:
7803
diff
changeset
|
641 |
XmlSupport.exportMap(fos, prefsCache); |
e5ec12a932da
7021209: convert lang, math, util to use try-with-resources
smarks
parents:
7803
diff
changeset
|
642 |
} |
2 | 643 |
if (!tmpFile.renameTo(prefsFile)) |
644 |
throw new BackingStoreException("Can't rename " + |
|
645 |
tmpFile + " to " + prefsFile); |
|
646 |
} catch(Exception e) { |
|
647 |
if (e instanceof BackingStoreException) |
|
648 |
throw (BackingStoreException)e; |
|
649 |
throw new BackingStoreException(e); |
|
650 |
} |
|
651 |
return null; |
|
652 |
} |
|
653 |
}); |
|
654 |
} catch (PrivilegedActionException e) { |
|
655 |
throw (BackingStoreException) e.getException(); |
|
656 |
} |
|
657 |
} |
|
658 |
||
659 |
protected String[] keysSpi() { |
|
660 |
initCacheIfNecessary(); |
|
51 | 661 |
return prefsCache.keySet().toArray(new String[prefsCache.size()]); |
2 | 662 |
} |
663 |
||
664 |
protected String[] childrenNamesSpi() { |
|
51 | 665 |
return AccessController.doPrivileged( |
666 |
new PrivilegedAction<String[]>() { |
|
667 |
public String[] run() { |
|
7803
56bc97d69d93
6880112: Project Coin: Port JDK core library code to use diamond operator
smarks
parents:
5506
diff
changeset
|
668 |
List<String> result = new ArrayList<>(); |
2 | 669 |
File[] dirContents = dir.listFiles(); |
670 |
if (dirContents != null) { |
|
671 |
for (int i = 0; i < dirContents.length; i++) |
|
672 |
if (dirContents[i].isDirectory()) |
|
673 |
result.add(nodeName(dirContents[i].getName())); |
|
674 |
} |
|
675 |
return result.toArray(EMPTY_STRING_ARRAY); |
|
676 |
} |
|
677 |
}); |
|
678 |
} |
|
679 |
||
680 |
private static final String[] EMPTY_STRING_ARRAY = new String[0]; |
|
681 |
||
682 |
protected AbstractPreferences childSpi(String name) { |
|
683 |
return new FileSystemPreferences(this, name); |
|
684 |
} |
|
685 |
||
686 |
public void removeNode() throws BackingStoreException { |
|
687 |
synchronized (isUserNode()? userLockFile: systemLockFile) { |
|
688 |
// to remove a node we need an exclusive lock |
|
689 |
if (!lockFile(false)) |
|
690 |
throw(new BackingStoreException("Couldn't get file lock.")); |
|
691 |
try { |
|
692 |
super.removeNode(); |
|
693 |
} finally { |
|
694 |
unlockFile(); |
|
695 |
} |
|
696 |
} |
|
697 |
} |
|
698 |
||
699 |
/** |
|
700 |
* Called with file lock held (in addition to node locks). |
|
701 |
*/ |
|
702 |
protected void removeNodeSpi() throws BackingStoreException { |
|
703 |
try { |
|
51 | 704 |
AccessController.doPrivileged( |
705 |
new PrivilegedExceptionAction<Void>() { |
|
706 |
public Void run() throws BackingStoreException { |
|
2 | 707 |
if (changeLog.contains(nodeCreate)) { |
708 |
changeLog.remove(nodeCreate); |
|
709 |
nodeCreate = null; |
|
710 |
return null; |
|
711 |
} |
|
712 |
if (!dir.exists()) |
|
713 |
return null; |
|
714 |
prefsFile.delete(); |
|
715 |
tmpFile.delete(); |
|
716 |
// dir should be empty now. If it's not, empty it |
|
717 |
File[] junk = dir.listFiles(); |
|
718 |
if (junk.length != 0) { |
|
719 |
getLogger().warning( |
|
720 |
"Found extraneous files when removing node: " |
|
721 |
+ Arrays.asList(junk)); |
|
722 |
for (int i=0; i<junk.length; i++) |
|
723 |
junk[i].delete(); |
|
724 |
} |
|
725 |
if (!dir.delete()) |
|
726 |
throw new BackingStoreException("Couldn't delete dir: " |
|
727 |
+ dir); |
|
728 |
return null; |
|
729 |
} |
|
730 |
}); |
|
731 |
} catch (PrivilegedActionException e) { |
|
732 |
throw (BackingStoreException) e.getException(); |
|
733 |
} |
|
734 |
} |
|
735 |
||
736 |
public synchronized void sync() throws BackingStoreException { |
|
737 |
boolean userNode = isUserNode(); |
|
738 |
boolean shared; |
|
739 |
||
740 |
if (userNode) { |
|
741 |
shared = false; /* use exclusive lock for user prefs */ |
|
742 |
} else { |
|
743 |
/* if can write to system root, use exclusive lock. |
|
744 |
otherwise use shared lock. */ |
|
745 |
shared = !isSystemRootWritable; |
|
746 |
} |
|
747 |
synchronized (isUserNode()? userLockFile:systemLockFile) { |
|
748 |
if (!lockFile(shared)) |
|
749 |
throw(new BackingStoreException("Couldn't get file lock.")); |
|
750 |
final Long newModTime = |
|
51 | 751 |
AccessController.doPrivileged( |
752 |
new PrivilegedAction<Long>() { |
|
753 |
public Long run() { |
|
2 | 754 |
long nmt; |
755 |
if (isUserNode()) { |
|
756 |
nmt = userRootModFile.lastModified(); |
|
757 |
isUserRootModified = userRootModTime == nmt; |
|
758 |
} else { |
|
759 |
nmt = systemRootModFile.lastModified(); |
|
760 |
isSystemRootModified = systemRootModTime == nmt; |
|
761 |
} |
|
25186
63e1a2ec30f5
8048267: Replace uses of 'new Long()' with appropriate alternative across core classes
prappo
parents:
23582
diff
changeset
|
762 |
return nmt; |
2 | 763 |
} |
764 |
}); |
|
765 |
try { |
|
766 |
super.sync(); |
|
51 | 767 |
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
768 |
public Void run() { |
|
2 | 769 |
if (isUserNode()) { |
770 |
userRootModTime = newModTime.longValue() + 1000; |
|
771 |
userRootModFile.setLastModified(userRootModTime); |
|
772 |
} else { |
|
773 |
systemRootModTime = newModTime.longValue() + 1000; |
|
774 |
systemRootModFile.setLastModified(systemRootModTime); |
|
775 |
} |
|
776 |
return null; |
|
777 |
} |
|
778 |
}); |
|
779 |
} finally { |
|
780 |
unlockFile(); |
|
781 |
} |
|
782 |
} |
|
783 |
} |
|
784 |
||
785 |
protected void syncSpi() throws BackingStoreException { |
|
786 |
try { |
|
51 | 787 |
AccessController.doPrivileged( |
788 |
new PrivilegedExceptionAction<Void>() { |
|
789 |
public Void run() throws BackingStoreException { |
|
2 | 790 |
syncSpiPrivileged(); |
791 |
return null; |
|
792 |
} |
|
793 |
}); |
|
794 |
} catch (PrivilegedActionException e) { |
|
795 |
throw (BackingStoreException) e.getException(); |
|
796 |
} |
|
797 |
} |
|
798 |
private void syncSpiPrivileged() throws BackingStoreException { |
|
799 |
if (isRemoved()) |
|
800 |
throw new IllegalStateException("Node has been removed"); |
|
801 |
if (prefsCache == null) |
|
802 |
return; // We've never been used, don't bother syncing |
|
803 |
long lastModifiedTime; |
|
804 |
if ((isUserNode() ? isUserRootModified : isSystemRootModified)) { |
|
805 |
lastModifiedTime = prefsFile.lastModified(); |
|
806 |
if (lastModifiedTime != lastSyncTime) { |
|
807 |
// Prefs at this node were externally modified; read in node and |
|
808 |
// playback any local mods since last sync |
|
809 |
loadCache(); |
|
810 |
replayChanges(); |
|
811 |
lastSyncTime = lastModifiedTime; |
|
812 |
} |
|
813 |
} else if (lastSyncTime != 0 && !dir.exists()) { |
|
814 |
// This node was removed in the background. Playback any changes |
|
815 |
// against a virgin (empty) Map. |
|
7803
56bc97d69d93
6880112: Project Coin: Port JDK core library code to use diamond operator
smarks
parents:
5506
diff
changeset
|
816 |
prefsCache = new TreeMap<>(); |
2 | 817 |
replayChanges(); |
818 |
} |
|
819 |
if (!changeLog.isEmpty()) { |
|
820 |
writeBackCache(); // Creates directory & file if necessary |
|
821 |
/* |
|
822 |
* Attempt succeeded; it's barely possible that the call to |
|
823 |
* lastModified might fail (i.e., return 0), but this would not |
|
824 |
* be a disaster, as lastSyncTime is allowed to lag. |
|
825 |
*/ |
|
826 |
lastModifiedTime = prefsFile.lastModified(); |
|
827 |
/* If lastSyncTime did not change, or went back |
|
828 |
* increment by 1 second. Since we hold the lock |
|
829 |
* lastSyncTime always monotonically encreases in the |
|
830 |
* atomic sense. |
|
831 |
*/ |
|
832 |
if (lastSyncTime <= lastModifiedTime) { |
|
833 |
lastSyncTime = lastModifiedTime + 1000; |
|
834 |
prefsFile.setLastModified(lastSyncTime); |
|
835 |
} |
|
836 |
changeLog.clear(); |
|
837 |
} |
|
838 |
} |
|
839 |
||
840 |
public void flush() throws BackingStoreException { |
|
841 |
if (isRemoved()) |
|
842 |
return; |
|
843 |
sync(); |
|
844 |
} |
|
845 |
||
846 |
protected void flushSpi() throws BackingStoreException { |
|
847 |
// assert false; |
|
848 |
} |
|
849 |
||
850 |
/** |
|
851 |
* Returns true if the specified character is appropriate for use in |
|
852 |
* Unix directory names. A character is appropriate if it's a printable |
|
853 |
* ASCII character (> 0x1f && < 0x7f) and unequal to slash ('/', 0x2f), |
|
854 |
* dot ('.', 0x2e), or underscore ('_', 0x5f). |
|
855 |
*/ |
|
856 |
private static boolean isDirChar(char ch) { |
|
857 |
return ch > 0x1f && ch < 0x7f && ch != '/' && ch != '.' && ch != '_'; |
|
858 |
} |
|
859 |
||
860 |
/** |
|
861 |
* Returns the directory name corresponding to the specified node name. |
|
862 |
* Generally, this is just the node name. If the node name includes |
|
863 |
* inappropriate characters (as per isDirChar) it is translated to Base64. |
|
864 |
* with the underscore character ('_', 0x5f) prepended. |
|
865 |
*/ |
|
866 |
private static String dirName(String nodeName) { |
|
867 |
for (int i=0, n=nodeName.length(); i < n; i++) |
|
868 |
if (!isDirChar(nodeName.charAt(i))) |
|
869 |
return "_" + Base64.byteArrayToAltBase64(byteArray(nodeName)); |
|
870 |
return nodeName; |
|
871 |
} |
|
872 |
||
873 |
/** |
|
874 |
* Translate a string into a byte array by translating each character |
|
875 |
* into two bytes, high-byte first ("big-endian"). |
|
876 |
*/ |
|
877 |
private static byte[] byteArray(String s) { |
|
878 |
int len = s.length(); |
|
879 |
byte[] result = new byte[2*len]; |
|
880 |
for (int i=0, j=0; i<len; i++) { |
|
881 |
char c = s.charAt(i); |
|
882 |
result[j++] = (byte) (c>>8); |
|
883 |
result[j++] = (byte) c; |
|
884 |
} |
|
885 |
return result; |
|
886 |
} |
|
887 |
||
888 |
/** |
|
889 |
* Returns the node name corresponding to the specified directory name. |
|
22951
5fd21112b2b6
8034043: Native methods for preferences API should not be in libjava
alanb
parents:
9035
diff
changeset
|
890 |
* (Inverts the transformation of dirName(String). |
2 | 891 |
*/ |
892 |
private static String nodeName(String dirName) { |
|
893 |
if (dirName.charAt(0) != '_') |
|
894 |
return dirName; |
|
895 |
byte a[] = Base64.altBase64ToByteArray(dirName.substring(1)); |
|
896 |
StringBuffer result = new StringBuffer(a.length/2); |
|
897 |
for (int i = 0; i < a.length; ) { |
|
898 |
int highByte = a[i++] & 0xff; |
|
899 |
int lowByte = a[i++] & 0xff; |
|
900 |
result.append((char) ((highByte << 8) | lowByte)); |
|
901 |
} |
|
902 |
return result.toString(); |
|
903 |
} |
|
904 |
||
905 |
/** |
|
906 |
* Try to acquire the appropriate file lock (user or system). If |
|
907 |
* the initial attempt fails, several more attempts are made using |
|
908 |
* an exponential backoff strategy. If all attempts fail, this method |
|
909 |
* returns false. |
|
910 |
* @throws SecurityException if file access denied. |
|
911 |
*/ |
|
912 |
private boolean lockFile(boolean shared) throws SecurityException{ |
|
913 |
boolean usernode = isUserNode(); |
|
914 |
int[] result; |
|
915 |
int errorCode = 0; |
|
916 |
File lockFile = (usernode ? userLockFile : systemLockFile); |
|
917 |
long sleepTime = INIT_SLEEP_TIME; |
|
918 |
for (int i = 0; i < MAX_ATTEMPTS; i++) { |
|
919 |
try { |
|
920 |
int perm = (usernode? USER_READ_WRITE: USER_RW_ALL_READ); |
|
921 |
result = lockFile0(lockFile.getCanonicalPath(), perm, shared); |
|
922 |
||
923 |
errorCode = result[ERROR_CODE]; |
|
924 |
if (result[LOCK_HANDLE] != 0) { |
|
925 |
if (usernode) { |
|
926 |
userRootLockHandle = result[LOCK_HANDLE]; |
|
927 |
} else { |
|
928 |
systemRootLockHandle = result[LOCK_HANDLE]; |
|
929 |
} |
|
930 |
return true; |
|
931 |
} |
|
932 |
} catch(IOException e) { |
|
933 |
// // If at first, you don't succeed... |
|
934 |
} |
|
935 |
||
936 |
try { |
|
937 |
Thread.sleep(sleepTime); |
|
938 |
} catch(InterruptedException e) { |
|
939 |
checkLockFile0ErrorCode(errorCode); |
|
940 |
return false; |
|
941 |
} |
|
942 |
sleepTime *= 2; |
|
943 |
} |
|
944 |
checkLockFile0ErrorCode(errorCode); |
|
945 |
return false; |
|
946 |
} |
|
947 |
||
948 |
/** |
|
949 |
* Checks if unlockFile0() returned an error. Throws a SecurityException, |
|
950 |
* if access denied. Logs a warning otherwise. |
|
951 |
*/ |
|
952 |
private void checkLockFile0ErrorCode (int errorCode) |
|
953 |
throws SecurityException { |
|
954 |
if (errorCode == EACCES) |
|
955 |
throw new SecurityException("Could not lock " + |
|
956 |
(isUserNode()? "User prefs." : "System prefs.") + |
|
957 |
" Lock file access denied."); |
|
958 |
if (errorCode != EAGAIN) |
|
959 |
getLogger().warning("Could not lock " + |
|
960 |
(isUserNode()? "User prefs. " : "System prefs.") + |
|
961 |
" Unix error code " + errorCode + "."); |
|
962 |
} |
|
963 |
||
964 |
/** |
|
965 |
* Locks file using UNIX file locking. |
|
966 |
* @param fileName Absolute file name of the lock file. |
|
967 |
* @return Returns a lock handle, used to unlock the file. |
|
968 |
*/ |
|
969 |
private static native int[] |
|
970 |
lockFile0(String fileName, int permission, boolean shared); |
|
971 |
||
972 |
/** |
|
973 |
* Unlocks file previously locked by lockFile0(). |
|
974 |
* @param lockHandle Handle to the file lock. |
|
975 |
* @return Returns zero if OK, UNIX error code if failure. |
|
976 |
*/ |
|
977 |
private static native int unlockFile0(int lockHandle); |
|
978 |
||
979 |
/** |
|
980 |
* Changes UNIX file permissions. |
|
981 |
*/ |
|
982 |
private static native int chmod(String fileName, int permission); |
|
983 |
||
984 |
/** |
|
985 |
* Initial time between lock attempts, in ms. The time is doubled |
|
986 |
* after each failing attempt (except the first). |
|
987 |
*/ |
|
988 |
private static int INIT_SLEEP_TIME = 50; |
|
989 |
||
990 |
/** |
|
991 |
* Maximum number of lock attempts. |
|
992 |
*/ |
|
993 |
private static int MAX_ATTEMPTS = 5; |
|
994 |
||
995 |
/** |
|
28059
e576535359cc
8067377: My hobby: caning, then then canning, the the can-can
martin
parents:
25859
diff
changeset
|
996 |
* Release the appropriate file lock (user or system). |
2 | 997 |
* @throws SecurityException if file access denied. |
998 |
*/ |
|
999 |
private void unlockFile() { |
|
1000 |
int result; |
|
1001 |
boolean usernode = isUserNode(); |
|
1002 |
File lockFile = (usernode ? userLockFile : systemLockFile); |
|
1003 |
int lockHandle = ( usernode ? userRootLockHandle:systemRootLockHandle); |
|
1004 |
if (lockHandle == 0) { |
|
1005 |
getLogger().warning("Unlock: zero lockHandle for " + |
|
1006 |
(usernode ? "user":"system") + " preferences.)"); |
|
1007 |
return; |
|
1008 |
} |
|
1009 |
result = unlockFile0(lockHandle); |
|
1010 |
if (result != 0) { |
|
1011 |
getLogger().warning("Could not drop file-lock on " + |
|
1012 |
(isUserNode() ? "user" : "system") + " preferences." + |
|
1013 |
" Unix error code " + result + "."); |
|
1014 |
if (result == EACCES) |
|
1015 |
throw new SecurityException("Could not unlock" + |
|
1016 |
(isUserNode()? "User prefs." : "System prefs.") + |
|
1017 |
" Lock file access denied."); |
|
1018 |
} |
|
1019 |
if (isUserNode()) { |
|
1020 |
userRootLockHandle = 0; |
|
1021 |
} else { |
|
1022 |
systemRootLockHandle = 0; |
|
1023 |
} |
|
1024 |
} |
|
1025 |
} |