Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.prefs/macosx/classes/java/util/prefs/MacOSXPreferencesFile.java
41159 views
1
/*
2
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
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
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
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
*
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.
24
*/
25
26
package java.util.prefs;
27
28
import java.util.HashMap;
29
import java.util.HashSet;
30
import java.util.Iterator;
31
import java.util.Timer;
32
import java.util.TimerTask;
33
import java.lang.ref.WeakReference;
34
35
36
/*
37
MacOSXPreferencesFile synchronization:
38
39
Everything is synchronized on MacOSXPreferencesFile.class. This prevents:
40
* simultaneous updates to cachedFiles or changedFiles
41
* simultaneous creation of two objects for the same name+user+host triplet
42
* simultaneous modifications to the same file
43
* modifications during syncWorld/flushWorld
44
* (in MacOSXPreferences.removeNodeSpi()) modification or sync during
45
multi-step node removal process
46
... among other things.
47
*/
48
/*
49
Timers. There are two timers that control synchronization of prefs data to
50
and from disk.
51
52
* Sync timer periodically calls syncWorld() to force external disk changes
53
(e.g. from another VM) into the memory cache. The sync timer runs even
54
if there are no outstanding local changes. The sync timer syncs all live
55
MacOSXPreferencesFile objects (the cachedFiles list).
56
The sync timer period is controlled by the java.util.prefs.syncInterval
57
property (same as FileSystemPreferences). By default there is *no*
58
sync timer (unlike FileSystemPreferences); it is only enabled if the
59
syncInterval property is set. The minimum interval is 5 seconds.
60
61
* Flush timer calls flushWorld() to force local changes to disk.
62
The flush timer is scheduled to fire some time after each pref change,
63
unless it's already scheduled to fire before that. syncWorld and
64
flushWorld will cancel any outstanding flush timer as unnecessary.
65
The flush timer flushes all changed files (the changedFiles list).
66
The time between pref write and flush timer call is controlled by the
67
java.util.prefs.flushDelay property (unlike FileSystemPreferences).
68
The default is 60 seconds and the minimum is 5 seconds.
69
70
The flush timer's behavior is required by the Java Preferences spec
71
("changes will eventually propagate to the persistent backing store with
72
an implementation-dependent delay"). The sync timer is not required by
73
the spec (multiple VMs are only required to not corrupt the prefs), but
74
the periodic sync is implemented by FileSystemPreferences and may be
75
useful to some programs. The sync timer is disabled by default because
76
it's expensive and is usually not necessary.
77
*/
78
79
@SuppressWarnings("removal")
80
class MacOSXPreferencesFile {
81
82
static {
83
java.security.AccessController.doPrivileged(
84
new java.security.PrivilegedAction<Void>() {
85
public Void run() {
86
System.loadLibrary("prefs");
87
return null;
88
}
89
});
90
}
91
92
private class FlushTask extends TimerTask {
93
public void run() {
94
MacOSXPreferencesFile.flushWorld();
95
}
96
}
97
98
private class SyncTask extends TimerTask {
99
public void run() {
100
MacOSXPreferencesFile.syncWorld();
101
}
102
}
103
104
// Maps string -> weak reference to MacOSXPreferencesFile
105
private static HashMap<String, WeakReference<MacOSXPreferencesFile>>
106
cachedFiles;
107
// Files that may have unflushed changes
108
private static HashSet<MacOSXPreferencesFile> changedFiles;
109
110
111
// Timer and pending sync and flush tasks (which are both scheduled
112
// on the same timer)
113
private static Timer timer = null;
114
private static FlushTask flushTimerTask = null;
115
private static long flushDelay = -1; // in seconds (min 5, default 60)
116
private static long syncInterval = -1; // (min 5, default negative == off)
117
118
private String appName;
119
private long user;
120
private long host;
121
122
String name() { return appName; }
123
long user() { return user; }
124
long host() { return host; }
125
126
// private constructor - use factory method getFile() instead
127
private MacOSXPreferencesFile(String newName, long newUser, long newHost)
128
{
129
appName = newName;
130
user = newUser;
131
host = newHost;
132
}
133
134
// Factory method
135
// Always returns the same object for the given name+user+host
136
static synchronized MacOSXPreferencesFile
137
getFile(String newName, boolean isUser)
138
{
139
MacOSXPreferencesFile result = null;
140
141
if (cachedFiles == null)
142
cachedFiles = new HashMap<>();
143
144
String hashkey =
145
newName + String.valueOf(isUser);
146
WeakReference<MacOSXPreferencesFile> hashvalue = cachedFiles.get(hashkey);
147
if (hashvalue != null) {
148
result = hashvalue.get();
149
}
150
if (result == null) {
151
// Java user node == CF current user, any host
152
// Java system node == CF any user, current host
153
result = new MacOSXPreferencesFile(newName,
154
isUser ? cfCurrentUser : cfAnyUser,
155
isUser ? cfAnyHost : cfCurrentHost);
156
cachedFiles.put(hashkey, new WeakReference<MacOSXPreferencesFile>(result));
157
}
158
159
// Don't schedule this file for flushing until some nodes or
160
// keys are added to it.
161
162
// Do set up the sync timer if requested; sync timer affects reads
163
// as well as writes.
164
initSyncTimerIfNeeded();
165
166
return result;
167
}
168
169
170
// Write all prefs changes to disk and clear all cached prefs values
171
// (so the next read will read from disk).
172
static synchronized boolean syncWorld()
173
{
174
boolean ok = true;
175
176
if (cachedFiles != null && !cachedFiles.isEmpty()) {
177
Iterator<WeakReference<MacOSXPreferencesFile>> iter =
178
cachedFiles.values().iterator();
179
while (iter.hasNext()) {
180
WeakReference<MacOSXPreferencesFile> ref = iter.next();
181
MacOSXPreferencesFile f = ref.get();
182
if (f != null) {
183
if (!f.synchronize()) ok = false;
184
} else {
185
iter.remove();
186
}
187
}
188
}
189
190
// Kill any pending flush
191
if (flushTimerTask != null) {
192
flushTimerTask.cancel();
193
flushTimerTask = null;
194
}
195
196
// Clear changed file list. The changed files were guaranteed to
197
// have been in the cached file list (because there was a strong
198
// reference from changedFiles.
199
if (changedFiles != null) changedFiles.clear();
200
201
return ok;
202
}
203
204
205
// Sync only current user preferences
206
static synchronized boolean syncUser() {
207
boolean ok = true;
208
if (cachedFiles != null && !cachedFiles.isEmpty()) {
209
Iterator<WeakReference<MacOSXPreferencesFile>> iter =
210
cachedFiles.values().iterator();
211
while (iter.hasNext()) {
212
WeakReference<MacOSXPreferencesFile> ref = iter.next();
213
MacOSXPreferencesFile f = ref.get();
214
if (f != null && f.user == cfCurrentUser) {
215
if (!f.synchronize()) {
216
ok = false;
217
}
218
} else {
219
iter.remove();
220
}
221
}
222
}
223
// Remove synchronized file from changed file list. The changed files were
224
// guaranteed to have been in the cached file list (because there was a strong
225
// reference from changedFiles.
226
if (changedFiles != null) {
227
Iterator<MacOSXPreferencesFile> iterChanged = changedFiles.iterator();
228
while (iterChanged.hasNext()) {
229
MacOSXPreferencesFile f = iterChanged.next();
230
if (f != null && f.user == cfCurrentUser)
231
iterChanged.remove();
232
}
233
}
234
return ok;
235
}
236
237
//Flush only current user preferences
238
static synchronized boolean flushUser() {
239
boolean ok = true;
240
if (changedFiles != null && !changedFiles.isEmpty()) {
241
Iterator<MacOSXPreferencesFile> iterator = changedFiles.iterator();
242
while(iterator.hasNext()) {
243
MacOSXPreferencesFile f = iterator.next();
244
if (f.user == cfCurrentUser) {
245
if (!f.synchronize())
246
ok = false;
247
else
248
iterator.remove();
249
}
250
}
251
}
252
return ok;
253
}
254
255
// Write all prefs changes to disk, but do not clear all cached prefs
256
// values. Also kills any scheduled flush task.
257
// There's no CFPreferencesFlush() (<rdar://problem/3049129>), so lots of cached prefs
258
// are cleared anyway.
259
static synchronized boolean flushWorld()
260
{
261
boolean ok = true;
262
263
if (changedFiles != null && !changedFiles.isEmpty()) {
264
for (MacOSXPreferencesFile f : changedFiles) {
265
if (!f.synchronize())
266
ok = false;
267
}
268
changedFiles.clear();
269
}
270
271
if (flushTimerTask != null) {
272
flushTimerTask.cancel();
273
flushTimerTask = null;
274
}
275
276
return ok;
277
}
278
279
// Mark this prefs file as changed. The changes will be flushed in
280
// at most flushDelay() seconds.
281
// Must be called when synchronized on MacOSXPreferencesFile.class
282
private void markChanged()
283
{
284
// Add this file to the changed file list
285
if (changedFiles == null)
286
changedFiles = new HashSet<>();
287
changedFiles.add(this);
288
289
// Schedule a new flush and a shutdown hook, if necessary
290
if (flushTimerTask == null) {
291
flushTimerTask = new FlushTask();
292
timer().schedule(flushTimerTask, flushDelay() * 1000);
293
}
294
}
295
296
// Return the flush delay, initializing from a property if necessary.
297
private static synchronized long flushDelay()
298
{
299
if (flushDelay == -1) {
300
try {
301
// flush delay >= 5, default 60
302
flushDelay = Math.max(5, Integer.parseInt(System.getProperty("java.util.prefs.flushDelay", "60")));
303
} catch (NumberFormatException e) {
304
flushDelay = 60;
305
}
306
}
307
return flushDelay;
308
}
309
310
// Initialize and run the sync timer, if the sync timer property is set
311
// and the sync timer hasn't already been started.
312
private static synchronized void initSyncTimerIfNeeded()
313
{
314
// syncInterval: -1 is uninitialized, other negative is off,
315
// positive is seconds between syncs (min 5).
316
317
if (syncInterval == -1) {
318
try {
319
syncInterval = Integer.parseInt(System.getProperty("java.util.prefs.syncInterval", "-2"));
320
if (syncInterval >= 0) {
321
// minimum of 5 seconds
322
syncInterval = Math.max(5, syncInterval);
323
} else {
324
syncInterval = -2; // default off
325
}
326
} catch (NumberFormatException e) {
327
syncInterval = -2; // bad property value - default off
328
}
329
330
if (syncInterval > 0) {
331
timer().schedule(new TimerTask() {
332
@Override
333
public void run() {
334
MacOSXPreferencesFile.syncWorld();}
335
}, syncInterval * 1000, syncInterval * 1000);
336
} else {
337
// syncInterval property not set. No sync timer ever.
338
}
339
}
340
}
341
342
// Return the timer used for flush and sync, creating it if necessary.
343
private static synchronized Timer timer()
344
{
345
if (timer == null) {
346
timer = new Timer(true); // daemon
347
Thread flushThread =
348
new Thread(null, null, "Flush Thread", 0, false) {
349
@Override
350
public void run() {
351
flushWorld();
352
}
353
};
354
/* Set context class loader to null in order to avoid
355
* keeping a strong reference to an application classloader.
356
*/
357
flushThread.setContextClassLoader(null);
358
Runtime.getRuntime().addShutdownHook(flushThread);
359
}
360
return timer;
361
}
362
363
364
// Node manipulation
365
boolean addNode(String path)
366
{
367
synchronized(MacOSXPreferencesFile.class) {
368
markChanged();
369
return addNode(path, appName, user, host);
370
}
371
}
372
373
void removeNode(String path)
374
{
375
synchronized(MacOSXPreferencesFile.class) {
376
markChanged();
377
removeNode(path, appName, user, host);
378
}
379
}
380
381
boolean addChildToNode(String path, String child)
382
{
383
synchronized(MacOSXPreferencesFile.class) {
384
markChanged();
385
return addChildToNode(path, child+"/", appName, user, host);
386
}
387
}
388
389
void removeChildFromNode(String path, String child)
390
{
391
synchronized(MacOSXPreferencesFile.class) {
392
markChanged();
393
removeChildFromNode(path, child+"/", appName, user, host);
394
}
395
}
396
397
398
// Key manipulation
399
void addKeyToNode(String path, String key, String value)
400
{
401
synchronized(MacOSXPreferencesFile.class) {
402
markChanged();
403
addKeyToNode(path, key, value, appName, user, host);
404
}
405
}
406
407
void removeKeyFromNode(String path, String key)
408
{
409
synchronized(MacOSXPreferencesFile.class) {
410
markChanged();
411
removeKeyFromNode(path, key, appName, user, host);
412
}
413
}
414
415
String getKeyFromNode(String path, String key)
416
{
417
synchronized(MacOSXPreferencesFile.class) {
418
return getKeyFromNode(path, key, appName, user, host);
419
}
420
}
421
422
423
// Enumerators
424
String[] getChildrenForNode(String path)
425
{
426
synchronized(MacOSXPreferencesFile.class) {
427
return getChildrenForNode(path, appName, user, host);
428
}
429
}
430
431
String[] getKeysForNode(String path)
432
{
433
synchronized(MacOSXPreferencesFile.class) {
434
return getKeysForNode(path, appName, user, host);
435
}
436
}
437
438
439
// Synchronization
440
boolean synchronize()
441
{
442
synchronized(MacOSXPreferencesFile.class) {
443
return synchronize(appName, user, host);
444
}
445
}
446
447
448
// CF functions
449
// Must be called when synchronized on MacOSXPreferencesFile.class
450
private static final native boolean
451
addNode(String path, String name, long user, long host);
452
private static final native void
453
removeNode(String path, String name, long user, long host);
454
private static final native boolean
455
addChildToNode(String path, String child,
456
String name, long user, long host);
457
private static final native void
458
removeChildFromNode(String path, String child,
459
String name, long user, long host);
460
private static final native void
461
addKeyToNode(String path, String key, String value,
462
String name, long user, long host);
463
private static final native void
464
removeKeyFromNode(String path, String key,
465
String name, long user, long host);
466
private static final native String
467
getKeyFromNode(String path, String key,
468
String name, long user, long host);
469
private static final native String[]
470
getChildrenForNode(String path, String name, long user, long host);
471
private static final native String[]
472
getKeysForNode(String path, String name, long user, long host);
473
private static final native boolean
474
synchronize(String name, long user, long host);
475
476
// CFPreferences host and user values (CFStringRefs)
477
private static long cfCurrentUser = currentUser();
478
private static long cfAnyUser = anyUser();
479
private static long cfCurrentHost = currentHost();
480
private static long cfAnyHost = anyHost();
481
482
// CFPreferences constant accessors
483
private static final native long currentUser();
484
private static final native long anyUser();
485
private static final native long currentHost();
486
private static final native long anyHost();
487
}
488
489
490