Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.prefs/windows/classes/java/util/prefs/WindowsPreferences.java
41159 views
1
/*
2
* Copyright (c) 2000, 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.StringTokenizer;
29
import java.io.ByteArrayOutputStream;
30
import java.security.AccessController;
31
import java.security.PrivilegedAction;
32
33
import sun.util.logging.PlatformLogger;
34
35
/**
36
* Windows registry based implementation of {@code Preferences}.
37
* {@code Preferences}' {@code systemRoot} and {@code userRoot} are stored in
38
* {@code HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs} and
39
* {@code HKEY_CURRENT_USER\Software\JavaSoft\Prefs} correspondingly.
40
*
41
* @author Konstantin Kladko
42
* @see Preferences
43
* @see PreferencesFactory
44
* @since 1.4
45
*/
46
47
@SuppressWarnings("removal")
48
class WindowsPreferences extends AbstractPreferences {
49
50
static {
51
PrivilegedAction<Void> load = () -> {
52
System.loadLibrary("prefs");
53
return null;
54
};
55
AccessController.doPrivileged(load);
56
}
57
58
/**
59
* Logger for error messages
60
*/
61
private static PlatformLogger logger;
62
63
/**
64
* Windows registry path to {@code Preferences}'s root nodes.
65
*/
66
private static final byte[] WINDOWS_ROOT_PATH =
67
stringToByteArray("Software\\JavaSoft\\Prefs");
68
69
/**
70
* Windows handles to {@code HKEY_CURRENT_USER} and
71
* {@code HKEY_LOCAL_MACHINE} hives.
72
*/
73
private static final int HKEY_CURRENT_USER = 0x80000001;
74
private static final int HKEY_LOCAL_MACHINE = 0x80000002;
75
76
/**
77
* Mount point for {@code Preferences}' user root.
78
*/
79
private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER;
80
81
/**
82
* Mount point for {@code Preferences}' system root.
83
*/
84
private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE;
85
86
/**
87
* Maximum byte-encoded path length for Windows native functions,
88
* ending {@code null} character not included.
89
*/
90
private static final int MAX_WINDOWS_PATH_LENGTH = 256;
91
92
/**
93
* User root node.
94
*/
95
private static volatile Preferences userRoot;
96
97
static Preferences getUserRoot() {
98
Preferences root = userRoot;
99
if (root == null) {
100
synchronized (WindowsPreferences.class) {
101
root = userRoot;
102
if (root == null) {
103
root = new WindowsPreferences(USER_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
104
userRoot = root;
105
}
106
}
107
}
108
return root;
109
}
110
111
/**
112
* System root node.
113
*/
114
private static volatile Preferences systemRoot;
115
116
static Preferences getSystemRoot() {
117
Preferences root = systemRoot;
118
if (root == null) {
119
synchronized (WindowsPreferences.class) {
120
root = systemRoot;
121
if (root == null) {
122
root = new WindowsPreferences(SYSTEM_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
123
systemRoot = root;
124
}
125
}
126
}
127
return root;
128
}
129
130
/* Windows error codes. */
131
private static final int ERROR_SUCCESS = 0;
132
private static final int ERROR_FILE_NOT_FOUND = 2;
133
private static final int ERROR_ACCESS_DENIED = 5;
134
135
/* Constants used to interpret returns of native functions */
136
private static final int NATIVE_HANDLE = 0;
137
private static final int ERROR_CODE = 1;
138
private static final int SUBKEYS_NUMBER = 0;
139
private static final int VALUES_NUMBER = 2;
140
private static final int MAX_KEY_LENGTH = 3;
141
private static final int MAX_VALUE_NAME_LENGTH = 4;
142
private static final int DISPOSITION = 2;
143
private static final int REG_CREATED_NEW_KEY = 1;
144
private static final int REG_OPENED_EXISTING_KEY = 2;
145
private static final int NULL_NATIVE_HANDLE = 0;
146
147
/* Windows security masks */
148
private static final int DELETE = 0x10000;
149
private static final int KEY_QUERY_VALUE = 1;
150
private static final int KEY_SET_VALUE = 2;
151
private static final int KEY_CREATE_SUB_KEY = 4;
152
private static final int KEY_ENUMERATE_SUB_KEYS = 8;
153
private static final int KEY_READ = 0x20019;
154
private static final int KEY_WRITE = 0x20006;
155
private static final int KEY_ALL_ACCESS = 0xf003f;
156
157
/**
158
* Initial time between registry access attempts, in ms. The time is doubled
159
* after each failing attempt (except the first).
160
*/
161
private static int INIT_SLEEP_TIME = 50;
162
163
/**
164
* Maximum number of registry access attempts.
165
*/
166
private static int MAX_ATTEMPTS = 5;
167
168
/**
169
* BackingStore availability flag.
170
*/
171
private boolean isBackingStoreAvailable = true;
172
173
/**
174
* Java wrapper for Windows registry API RegOpenKey()
175
*/
176
private static native long[] WindowsRegOpenKey(long hKey, byte[] subKey,
177
int securityMask);
178
/**
179
* Retries RegOpenKey() MAX_ATTEMPTS times before giving up.
180
*/
181
private static long[] WindowsRegOpenKey1(long hKey, byte[] subKey,
182
int securityMask) {
183
long[] result = WindowsRegOpenKey(hKey, subKey, securityMask);
184
if (result[ERROR_CODE] == ERROR_SUCCESS) {
185
return result;
186
} else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) {
187
logger().warning("Trying to recreate Windows registry node " +
188
byteArrayToString(subKey) + " at root 0x" +
189
Long.toHexString(hKey) + ".");
190
// Try recreation
191
long handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE];
192
WindowsRegCloseKey(handle);
193
return WindowsRegOpenKey(hKey, subKey, securityMask);
194
} else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) {
195
long sleepTime = INIT_SLEEP_TIME;
196
for (int i = 0; i < MAX_ATTEMPTS; i++) {
197
try {
198
Thread.sleep(sleepTime);
199
} catch(InterruptedException e) {
200
return result;
201
}
202
sleepTime *= 2;
203
result = WindowsRegOpenKey(hKey, subKey, securityMask);
204
if (result[ERROR_CODE] == ERROR_SUCCESS) {
205
return result;
206
}
207
}
208
}
209
return result;
210
}
211
212
/**
213
* Java wrapper for Windows registry API RegCloseKey()
214
*/
215
private static native int WindowsRegCloseKey(long hKey);
216
217
/**
218
* Java wrapper for Windows registry API RegCreateKeyEx()
219
*/
220
private static native long[] WindowsRegCreateKeyEx(long hKey, byte[] subKey);
221
222
/**
223
* Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.
224
*/
225
private static long[] WindowsRegCreateKeyEx1(long hKey, byte[] subKey) {
226
long[] result = WindowsRegCreateKeyEx(hKey, subKey);
227
if (result[ERROR_CODE] == ERROR_SUCCESS) {
228
return result;
229
} else {
230
long sleepTime = INIT_SLEEP_TIME;
231
for (int i = 0; i < MAX_ATTEMPTS; i++) {
232
try {
233
Thread.sleep(sleepTime);
234
} catch(InterruptedException e) {
235
return result;
236
}
237
sleepTime *= 2;
238
result = WindowsRegCreateKeyEx(hKey, subKey);
239
if (result[ERROR_CODE] == ERROR_SUCCESS) {
240
return result;
241
}
242
}
243
}
244
return result;
245
}
246
/**
247
* Java wrapper for Windows registry API RegDeleteKey()
248
*/
249
private static native int WindowsRegDeleteKey(long hKey, byte[] subKey);
250
251
/**
252
* Java wrapper for Windows registry API RegFlushKey()
253
*/
254
private static native int WindowsRegFlushKey(long hKey);
255
256
/**
257
* Retries RegFlushKey() MAX_ATTEMPTS times before giving up.
258
*/
259
private static int WindowsRegFlushKey1(long hKey) {
260
int result = WindowsRegFlushKey(hKey);
261
if (result == ERROR_SUCCESS) {
262
return result;
263
} else {
264
long sleepTime = INIT_SLEEP_TIME;
265
for (int i = 0; i < MAX_ATTEMPTS; i++) {
266
try {
267
Thread.sleep(sleepTime);
268
} catch(InterruptedException e) {
269
return result;
270
}
271
sleepTime *= 2;
272
result = WindowsRegFlushKey(hKey);
273
if (result == ERROR_SUCCESS) {
274
return result;
275
}
276
}
277
}
278
return result;
279
}
280
281
/**
282
* Java wrapper for Windows registry API RegQueryValueEx()
283
*/
284
private static native byte[] WindowsRegQueryValueEx(long hKey,
285
byte[] valueName);
286
/**
287
* Java wrapper for Windows registry API RegSetValueEx()
288
*/
289
private static native int WindowsRegSetValueEx(long hKey, byte[] valueName,
290
byte[] value);
291
/**
292
* Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.
293
*/
294
private static int WindowsRegSetValueEx1(long hKey, byte[] valueName,
295
byte[] value) {
296
int result = WindowsRegSetValueEx(hKey, valueName, value);
297
if (result == ERROR_SUCCESS) {
298
return result;
299
} else {
300
long sleepTime = INIT_SLEEP_TIME;
301
for (int i = 0; i < MAX_ATTEMPTS; i++) {
302
try {
303
Thread.sleep(sleepTime);
304
} catch(InterruptedException e) {
305
return result;
306
}
307
sleepTime *= 2;
308
result = WindowsRegSetValueEx(hKey, valueName, value);
309
if (result == ERROR_SUCCESS) {
310
return result;
311
}
312
}
313
}
314
return result;
315
}
316
317
/**
318
* Java wrapper for Windows registry API RegDeleteValue()
319
*/
320
private static native int WindowsRegDeleteValue(long hKey, byte[] valueName);
321
322
/**
323
* Java wrapper for Windows registry API RegQueryInfoKey()
324
*/
325
private static native long[] WindowsRegQueryInfoKey(long hKey);
326
327
/**
328
* Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.
329
*/
330
private static long[] WindowsRegQueryInfoKey1(long hKey) {
331
long[] result = WindowsRegQueryInfoKey(hKey);
332
if (result[ERROR_CODE] == ERROR_SUCCESS) {
333
return result;
334
} else {
335
long sleepTime = INIT_SLEEP_TIME;
336
for (int i = 0; i < MAX_ATTEMPTS; i++) {
337
try {
338
Thread.sleep(sleepTime);
339
} catch(InterruptedException e) {
340
return result;
341
}
342
sleepTime *= 2;
343
result = WindowsRegQueryInfoKey(hKey);
344
if (result[ERROR_CODE] == ERROR_SUCCESS) {
345
return result;
346
}
347
}
348
}
349
return result;
350
}
351
352
/**
353
* Java wrapper for Windows registry API RegEnumKeyEx()
354
*/
355
private static native byte[] WindowsRegEnumKeyEx(long hKey, int subKeyIndex,
356
int maxKeyLength);
357
358
/**
359
* Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up.
360
*/
361
private static byte[] WindowsRegEnumKeyEx1(long hKey, int subKeyIndex,
362
int maxKeyLength) {
363
byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
364
if (result != null) {
365
return result;
366
} else {
367
long sleepTime = INIT_SLEEP_TIME;
368
for (int i = 0; i < MAX_ATTEMPTS; i++) {
369
try {
370
Thread.sleep(sleepTime);
371
} catch(InterruptedException e) {
372
return result;
373
}
374
sleepTime *= 2;
375
result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
376
if (result != null) {
377
return result;
378
}
379
}
380
}
381
return result;
382
}
383
384
/**
385
* Java wrapper for Windows registry API RegEnumValue()
386
*/
387
private static native byte[] WindowsRegEnumValue(long hKey, int valueIndex,
388
int maxValueNameLength);
389
/**
390
* Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.
391
*/
392
private static byte[] WindowsRegEnumValue1(long hKey, int valueIndex,
393
int maxValueNameLength) {
394
byte[] result = WindowsRegEnumValue(hKey, valueIndex,
395
maxValueNameLength);
396
if (result != null) {
397
return result;
398
} else {
399
long sleepTime = INIT_SLEEP_TIME;
400
for (int i = 0; i < MAX_ATTEMPTS; i++) {
401
try {
402
Thread.sleep(sleepTime);
403
} catch(InterruptedException e) {
404
return result;
405
}
406
sleepTime *= 2;
407
result = WindowsRegEnumValue(hKey, valueIndex,
408
maxValueNameLength);
409
if (result != null) {
410
return result;
411
}
412
}
413
}
414
return result;
415
}
416
417
/**
418
* Constructs a {@code WindowsPreferences} node, creating underlying
419
* Windows registry node and all its Windows parents, if they are not yet
420
* created.
421
* Logs a warning message, if Windows Registry is unavailable.
422
*/
423
private WindowsPreferences(WindowsPreferences parent, String name) {
424
super(parent, name);
425
long parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ);
426
if (parentNativeHandle == NULL_NATIVE_HANDLE) {
427
// if here, openKey failed and logged
428
isBackingStoreAvailable = false;
429
return;
430
}
431
long[] result =
432
WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name));
433
if (result[ERROR_CODE] != ERROR_SUCCESS) {
434
logger().warning("Could not create windows registry node " +
435
byteArrayToString(windowsAbsolutePath()) +
436
" at root 0x" + Long.toHexString(parentNativeHandle) +
437
". Windows RegCreateKeyEx(...) returned error code " +
438
result[ERROR_CODE] + ".");
439
isBackingStoreAvailable = false;
440
return;
441
}
442
newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
443
closeKey(parentNativeHandle);
444
closeKey(result[NATIVE_HANDLE]);
445
}
446
447
/**
448
* Constructs a root node creating the underlying
449
* Windows registry node and all of its parents, if they have not yet been
450
* created.
451
* Logs a warning message, if Windows Registry is unavailable.
452
* @param rootNativeHandle Native handle to one of Windows top level keys.
453
* @param rootDirectory Path to root directory, as a byte-encoded string.
454
*/
455
private WindowsPreferences(long rootNativeHandle, byte[] rootDirectory) {
456
super(null, "");
457
long[] result =
458
WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory);
459
if (result[ERROR_CODE] != ERROR_SUCCESS) {
460
logger().warning("Could not open/create prefs root node " +
461
byteArrayToString(windowsAbsolutePath()) +
462
" at root 0x" + Long.toHexString(rootNativeHandle) +
463
". Windows RegCreateKeyEx(...) returned error code " +
464
result[ERROR_CODE] + ".");
465
isBackingStoreAvailable = false;
466
return;
467
}
468
// Check if a new node
469
newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
470
closeKey(result[NATIVE_HANDLE]);
471
}
472
473
/**
474
* Returns Windows absolute path of the current node as a byte array.
475
* Java "/" separator is transformed into Windows "\".
476
* @see Preferences#absolutePath()
477
*/
478
private byte[] windowsAbsolutePath() {
479
ByteArrayOutputStream bstream = new ByteArrayOutputStream();
480
bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);
481
StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/");
482
while (tokenizer.hasMoreTokens()) {
483
bstream.write((byte)'\\');
484
String nextName = tokenizer.nextToken();
485
byte[] windowsNextName = toWindowsName(nextName);
486
bstream.write(windowsNextName, 0, windowsNextName.length-1);
487
}
488
bstream.write(0);
489
return bstream.toByteArray();
490
}
491
492
/**
493
* Opens current node's underlying Windows registry key using a
494
* given security mask.
495
* @param securityMask Windows security mask.
496
* @return Windows registry key's handle.
497
* @see #openKey(byte[], int)
498
* @see #openKey(int, byte[], int)
499
* @see #closeKey(int)
500
*/
501
private long openKey(int securityMask) {
502
return openKey(securityMask, securityMask);
503
}
504
505
/**
506
* Opens current node's underlying Windows registry key using a
507
* given security mask.
508
* @param mask1 Preferred Windows security mask.
509
* @param mask2 Alternate Windows security mask.
510
* @return Windows registry key's handle.
511
* @see #openKey(byte[], int)
512
* @see #openKey(int, byte[], int)
513
* @see #closeKey(int)
514
*/
515
private long openKey(int mask1, int mask2) {
516
return openKey(windowsAbsolutePath(), mask1, mask2);
517
}
518
519
/**
520
* Opens Windows registry key at a given absolute path using a given
521
* security mask.
522
* @param windowsAbsolutePath Windows absolute path of the
523
* key as a byte-encoded string.
524
* @param mask1 Preferred Windows security mask.
525
* @param mask2 Alternate Windows security mask.
526
* @return Windows registry key's handle.
527
* @see #openKey(int)
528
* @see #openKey(int, byte[],int)
529
* @see #closeKey(int)
530
*/
531
private long openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {
532
/* Check if key's path is short enough be opened at once
533
otherwise use a path-splitting procedure */
534
if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {
535
long[] result = WindowsRegOpenKey1(rootNativeHandle(),
536
windowsAbsolutePath, mask1);
537
if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
538
result = WindowsRegOpenKey1(rootNativeHandle(),
539
windowsAbsolutePath, mask2);
540
541
if (result[ERROR_CODE] != ERROR_SUCCESS) {
542
logger().warning("Could not open windows registry node " +
543
byteArrayToString(windowsAbsolutePath()) +
544
" at root 0x" +
545
Long.toHexString(rootNativeHandle()) +
546
". Windows RegOpenKey(...) returned error code " +
547
result[ERROR_CODE] + ".");
548
result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
549
if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {
550
throw new SecurityException(
551
"Could not open windows registry node " +
552
byteArrayToString(windowsAbsolutePath()) +
553
" at root 0x" +
554
Long.toHexString(rootNativeHandle()) +
555
": Access denied");
556
}
557
}
558
return result[NATIVE_HANDLE];
559
} else {
560
return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);
561
}
562
}
563
564
/**
565
* Opens Windows registry key at a given relative path
566
* with respect to a given Windows registry key.
567
* @param windowsAbsolutePath Windows relative path of the
568
* key as a byte-encoded string.
569
* @param nativeHandle handle to the base Windows key.
570
* @param mask1 Preferred Windows security mask.
571
* @param mask2 Alternate Windows security mask.
572
* @return Windows registry key's handle.
573
* @see #openKey(int)
574
* @see #openKey(byte[],int)
575
* @see #closeKey(int)
576
*/
577
private long openKey(long nativeHandle, byte[] windowsRelativePath,
578
int mask1, int mask2) {
579
/* If the path is short enough open at once. Otherwise split the path */
580
if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) {
581
long[] result = WindowsRegOpenKey1(nativeHandle,
582
windowsRelativePath, mask1);
583
if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
584
result = WindowsRegOpenKey1(nativeHandle,
585
windowsRelativePath, mask2);
586
587
if (result[ERROR_CODE] != ERROR_SUCCESS) {
588
logger().warning("Could not open windows registry node " +
589
byteArrayToString(windowsAbsolutePath()) +
590
" at root 0x" + Long.toHexString(nativeHandle) +
591
". Windows RegOpenKey(...) returned error code " +
592
result[ERROR_CODE] + ".");
593
result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
594
}
595
return result[NATIVE_HANDLE];
596
} else {
597
int separatorPosition = -1;
598
// Be greedy - open the longest possible path
599
for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {
600
if (windowsRelativePath[i] == ((byte)'\\')) {
601
separatorPosition = i;
602
break;
603
}
604
}
605
// Split the path and do the recursion
606
byte[] nextRelativeRoot = new byte[separatorPosition+1];
607
System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0,
608
separatorPosition);
609
nextRelativeRoot[separatorPosition] = 0;
610
byte[] nextRelativePath = new byte[windowsRelativePath.length -
611
separatorPosition - 1];
612
System.arraycopy(windowsRelativePath, separatorPosition+1,
613
nextRelativePath, 0, nextRelativePath.length);
614
long nextNativeHandle = openKey(nativeHandle, nextRelativeRoot,
615
mask1, mask2);
616
if (nextNativeHandle == NULL_NATIVE_HANDLE) {
617
return NULL_NATIVE_HANDLE;
618
}
619
long result = openKey(nextNativeHandle, nextRelativePath,
620
mask1,mask2);
621
closeKey(nextNativeHandle);
622
return result;
623
}
624
}
625
626
/**
627
* Closes Windows registry key.
628
* Logs a warning if Windows registry is unavailable.
629
* @param key's Windows registry handle.
630
* @see #openKey(int)
631
* @see #openKey(byte[],int)
632
* @see #openKey(int, byte[],int)
633
*/
634
private void closeKey(long nativeHandle) {
635
int result = WindowsRegCloseKey(nativeHandle);
636
if (result != ERROR_SUCCESS) {
637
logger().warning("Could not close windows registry node " +
638
byteArrayToString(windowsAbsolutePath()) +
639
" at root 0x" +
640
Long.toHexString(rootNativeHandle()) +
641
". Windows RegCloseKey(...) returned error code " +
642
result + ".");
643
}
644
}
645
646
/**
647
* Implements {@code AbstractPreferences} {@code putSpi()} method.
648
* Puts name-value pair into the underlying Windows registry node.
649
* Logs a warning, if Windows registry is unavailable.
650
* @see #getSpi(String)
651
*/
652
protected void putSpi(String javaName, String value) {
653
long nativeHandle = openKey(KEY_SET_VALUE);
654
if (nativeHandle == NULL_NATIVE_HANDLE) {
655
isBackingStoreAvailable = false;
656
return;
657
}
658
int result = WindowsRegSetValueEx1(nativeHandle,
659
toWindowsName(javaName), toWindowsValueString(value));
660
if (result != ERROR_SUCCESS) {
661
logger().warning("Could not assign value to key " +
662
byteArrayToString(toWindowsName(javaName)) +
663
" at Windows registry node " +
664
byteArrayToString(windowsAbsolutePath()) +
665
" at root 0x" +
666
Long.toHexString(rootNativeHandle()) +
667
". Windows RegSetValueEx(...) returned error code " +
668
result + ".");
669
isBackingStoreAvailable = false;
670
}
671
closeKey(nativeHandle);
672
}
673
674
/**
675
* Implements {@code AbstractPreferences} {@code getSpi()} method.
676
* Gets a string value from the underlying Windows registry node.
677
* Logs a warning, if Windows registry is unavailable.
678
* @see #putSpi(String, String)
679
*/
680
protected String getSpi(String javaName) {
681
long nativeHandle = openKey(KEY_QUERY_VALUE);
682
if (nativeHandle == NULL_NATIVE_HANDLE) {
683
return null;
684
}
685
Object resultObject = WindowsRegQueryValueEx(nativeHandle,
686
toWindowsName(javaName));
687
if (resultObject == null) {
688
closeKey(nativeHandle);
689
return null;
690
}
691
closeKey(nativeHandle);
692
return toJavaValueString((byte[]) resultObject);
693
}
694
695
/**
696
* Implements {@code AbstractPreferences} {@code removeSpi()} method.
697
* Deletes a string name-value pair from the underlying Windows registry
698
* node, if this value still exists.
699
* Logs a warning, if Windows registry is unavailable or key has already
700
* been deleted.
701
*/
702
protected void removeSpi(String key) {
703
long nativeHandle = openKey(KEY_SET_VALUE);
704
if (nativeHandle == NULL_NATIVE_HANDLE) {
705
return;
706
}
707
int result =
708
WindowsRegDeleteValue(nativeHandle, toWindowsName(key));
709
if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
710
logger().warning("Could not delete windows registry value " +
711
byteArrayToString(windowsAbsolutePath()) + "\\" +
712
toWindowsName(key) + " at root 0x" +
713
Long.toHexString(rootNativeHandle()) +
714
". Windows RegDeleteValue(...) returned error code " +
715
result + ".");
716
isBackingStoreAvailable = false;
717
}
718
closeKey(nativeHandle);
719
}
720
721
/**
722
* Implements {@code AbstractPreferences} {@code keysSpi()} method.
723
* Gets value names from the underlying Windows registry node.
724
* Throws a BackingStoreException and logs a warning, if
725
* Windows registry is unavailable.
726
*/
727
protected String[] keysSpi() throws BackingStoreException{
728
// Find out the number of values
729
long nativeHandle = openKey(KEY_QUERY_VALUE);
730
if (nativeHandle == NULL_NATIVE_HANDLE) {
731
throw new BackingStoreException(
732
"Could not open windows registry node " +
733
byteArrayToString(windowsAbsolutePath()) +
734
" at root 0x" +
735
Long.toHexString(rootNativeHandle()) + ".");
736
}
737
long[] result = WindowsRegQueryInfoKey1(nativeHandle);
738
if (result[ERROR_CODE] != ERROR_SUCCESS) {
739
String info = "Could not query windows registry node " +
740
byteArrayToString(windowsAbsolutePath()) +
741
" at root 0x" +
742
Long.toHexString(rootNativeHandle()) +
743
". Windows RegQueryInfoKeyEx(...) returned error code " +
744
result[ERROR_CODE] + ".";
745
logger().warning(info);
746
throw new BackingStoreException(info);
747
}
748
int maxValueNameLength = (int)result[MAX_VALUE_NAME_LENGTH];
749
int valuesNumber = (int)result[VALUES_NUMBER];
750
if (valuesNumber == 0) {
751
closeKey(nativeHandle);
752
return new String[0];
753
}
754
// Get the values
755
String[] valueNames = new String[valuesNumber];
756
for (int i = 0; i < valuesNumber; i++) {
757
byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,
758
maxValueNameLength+1);
759
if (windowsName == null) {
760
String info =
761
"Could not enumerate value #" + i + " of windows node " +
762
byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
763
Long.toHexString(rootNativeHandle()) + ".";
764
logger().warning(info);
765
throw new BackingStoreException(info);
766
}
767
valueNames[i] = toJavaName(windowsName);
768
}
769
closeKey(nativeHandle);
770
return valueNames;
771
}
772
773
/**
774
* Implements {@code AbstractPreferences} {@code childrenNamesSpi()} method.
775
* Calls Windows registry to retrive children of this node.
776
* Throws a BackingStoreException and logs a warning message,
777
* if Windows registry is not available.
778
*/
779
protected String[] childrenNamesSpi() throws BackingStoreException {
780
// Open key
781
long nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE);
782
if (nativeHandle == NULL_NATIVE_HANDLE) {
783
throw new BackingStoreException(
784
"Could not open windows registry node " +
785
byteArrayToString(windowsAbsolutePath()) +
786
" at root 0x" +
787
Long.toHexString(rootNativeHandle()) + ".");
788
}
789
// Get number of children
790
long[] result = WindowsRegQueryInfoKey1(nativeHandle);
791
if (result[ERROR_CODE] != ERROR_SUCCESS) {
792
String info = "Could not query windows registry node " +
793
byteArrayToString(windowsAbsolutePath()) +
794
" at root 0x" + Long.toHexString(rootNativeHandle()) +
795
". Windows RegQueryInfoKeyEx(...) returned error code " +
796
result[ERROR_CODE] + ".";
797
logger().warning(info);
798
throw new BackingStoreException(info);
799
}
800
int maxKeyLength = (int)result[MAX_KEY_LENGTH];
801
int subKeysNumber = (int)result[SUBKEYS_NUMBER];
802
if (subKeysNumber == 0) {
803
closeKey(nativeHandle);
804
return new String[0];
805
}
806
String[] subkeys = new String[subKeysNumber];
807
String[] children = new String[subKeysNumber];
808
// Get children
809
for (int i = 0; i < subKeysNumber; i++) {
810
byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,
811
maxKeyLength+1);
812
if (windowsName == null) {
813
String info =
814
"Could not enumerate key #" + i + " of windows node " +
815
byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
816
Long.toHexString(rootNativeHandle()) + ". ";
817
logger().warning(info);
818
throw new BackingStoreException(info);
819
}
820
String javaName = toJavaName(windowsName);
821
children[i] = javaName;
822
}
823
closeKey(nativeHandle);
824
return children;
825
}
826
827
/**
828
* Implements {@code Preferences} {@code flush()} method.
829
* Flushes Windows registry changes to disk.
830
* Throws a BackingStoreException and logs a warning message if Windows
831
* registry is not available.
832
*/
833
public void flush() throws BackingStoreException{
834
835
if (isRemoved()) {
836
parent.flush();
837
return;
838
}
839
if (!isBackingStoreAvailable) {
840
throw new BackingStoreException(
841
"flush(): Backing store not available.");
842
}
843
long nativeHandle = openKey(KEY_READ);
844
if (nativeHandle == NULL_NATIVE_HANDLE) {
845
throw new BackingStoreException(
846
"Could not open windows registry node " +
847
byteArrayToString(windowsAbsolutePath()) +
848
" at root 0x" +
849
Long.toHexString(rootNativeHandle()) + ".");
850
}
851
int result = WindowsRegFlushKey1(nativeHandle);
852
if (result != ERROR_SUCCESS) {
853
String info = "Could not flush windows registry node " +
854
byteArrayToString(windowsAbsolutePath()) +
855
" at root 0x" +
856
Long.toHexString(rootNativeHandle()) +
857
". Windows RegFlushKey(...) returned error code " +
858
result + ".";
859
logger().warning(info);
860
throw new BackingStoreException(info);
861
}
862
closeKey(nativeHandle);
863
}
864
865
866
/**
867
* Implements {@code Preferences} {@code sync()} method.
868
* Flushes Windows registry changes to disk. Equivalent to flush().
869
* @see flush()
870
*/
871
public void sync() throws BackingStoreException{
872
if (isRemoved())
873
throw new IllegalStateException("Node has been removed");
874
flush();
875
}
876
877
/**
878
* Implements {@code AbstractPreferences} {@code childSpi()} method.
879
* Constructs a child node with a
880
* given name and creates its underlying Windows registry node,
881
* if it does not exist.
882
* Logs a warning message, if Windows Registry is unavailable.
883
*/
884
protected AbstractPreferences childSpi(String name) {
885
return new WindowsPreferences(this, name);
886
}
887
888
/**
889
* Implements {@code AbstractPreferences} {@code removeNodeSpi()} method.
890
* Deletes underlying Windows registry node.
891
* Throws a BackingStoreException and logs a warning, if Windows registry
892
* is not available.
893
*/
894
public void removeNodeSpi() throws BackingStoreException {
895
long parentNativeHandle =
896
((WindowsPreferences)parent()).openKey(DELETE);
897
if (parentNativeHandle == NULL_NATIVE_HANDLE) {
898
throw new BackingStoreException(
899
"Could not open parent windows registry node of " +
900
byteArrayToString(windowsAbsolutePath()) +
901
" at root 0x" +
902
Long.toHexString(rootNativeHandle()) + ".");
903
}
904
int result =
905
WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name()));
906
if (result != ERROR_SUCCESS) {
907
String info = "Could not delete windows registry node " +
908
byteArrayToString(windowsAbsolutePath()) +
909
" at root 0x" + Long.toHexString(rootNativeHandle()) +
910
". Windows RegDeleteKeyEx(...) returned error code " +
911
result + ".";
912
logger().warning(info);
913
throw new BackingStoreException(info);
914
}
915
closeKey(parentNativeHandle);
916
}
917
918
/**
919
* Converts value's or node's name from its byte array representation to
920
* java string. Two encodings, simple and altBase64 are used. See
921
* {@link #toWindowsName(String) toWindowsName()} for a detailed
922
* description of encoding conventions.
923
* @param windowsNameArray Null-terminated byte array.
924
*/
925
private static String toJavaName(byte[] windowsNameArray) {
926
String windowsName = byteArrayToString(windowsNameArray);
927
// check if Alt64
928
if ((windowsName.length() > 1) &&
929
(windowsName.substring(0, 2).equals("/!"))) {
930
return toJavaAlt64Name(windowsName);
931
}
932
StringBuilder javaName = new StringBuilder();
933
char ch;
934
// Decode from simple encoding
935
for (int i = 0; i < windowsName.length(); i++) {
936
if ((ch = windowsName.charAt(i)) == '/') {
937
char next = ' ';
938
if ((windowsName.length() > i + 1) &&
939
((next = windowsName.charAt(i+1)) >= 'A') &&
940
(next <= 'Z')) {
941
ch = next;
942
i++;
943
} else if ((windowsName.length() > i + 1) &&
944
(next == '/')) {
945
ch = '\\';
946
i++;
947
}
948
} else if (ch == '\\') {
949
ch = '/';
950
}
951
javaName.append(ch);
952
}
953
return javaName.toString();
954
}
955
956
/**
957
* Converts value's or node's name from its Windows representation to java
958
* string, using altBase64 encoding. See
959
* {@link #toWindowsName(String) toWindowsName()} for a detailed
960
* description of encoding conventions.
961
*/
962
963
private static String toJavaAlt64Name(String windowsName) {
964
byte[] byteBuffer =
965
Base64.altBase64ToByteArray(windowsName.substring(2));
966
StringBuilder result = new StringBuilder();
967
for (int i = 0; i < byteBuffer.length; i++) {
968
int firstbyte = (byteBuffer[i++] & 0xff);
969
int secondbyte = (byteBuffer[i] & 0xff);
970
result.append((char)((firstbyte << 8) + secondbyte));
971
}
972
return result.toString();
973
}
974
975
/**
976
* Converts value's or node's name to its Windows representation
977
* as a byte-encoded string.
978
* Two encodings, simple and altBase64 are used.
979
* <p>
980
* <i>Simple</i> encoding is used, if java string does not contain
981
* any characters less, than 0x0020, or greater, than 0x007f.
982
* Simple encoding adds "/" character to capital letters, i.e.
983
* "A" is encoded as "/A". Character '\' is encoded as '//',
984
* '/' is encoded as '\'.
985
* The constructed string is converted to byte array by truncating the
986
* highest byte and adding the terminating {@code null} character.
987
* <p>
988
* <i>altBase64</i> encoding is used, if java string does contain at least
989
* one character less, than 0x0020, or greater, than 0x007f.
990
* This encoding is marked by setting first two bytes of the
991
* Windows string to '/!'. The java name is then encoded using
992
* byteArrayToAltBase64() method from
993
* Base64 class.
994
*/
995
private static byte[] toWindowsName(String javaName) {
996
StringBuilder windowsName = new StringBuilder();
997
for (int i = 0; i < javaName.length(); i++) {
998
char ch = javaName.charAt(i);
999
if ((ch < 0x0020) || (ch > 0x007f)) {
1000
// If a non-trivial character encountered, use altBase64
1001
return toWindowsAlt64Name(javaName);
1002
}
1003
if (ch == '\\') {
1004
windowsName.append("//");
1005
} else if (ch == '/') {
1006
windowsName.append('\\');
1007
} else if ((ch >= 'A') && (ch <='Z')) {
1008
windowsName.append('/').append(ch);
1009
} else {
1010
windowsName.append(ch);
1011
}
1012
}
1013
return stringToByteArray(windowsName.toString());
1014
}
1015
1016
/**
1017
* Converts value's or node's name to its Windows representation
1018
* as a byte-encoded string, using altBase64 encoding. See
1019
* {@link #toWindowsName(String) toWindowsName()} for a detailed
1020
* description of encoding conventions.
1021
*/
1022
private static byte[] toWindowsAlt64Name(String javaName) {
1023
byte[] javaNameArray = new byte[2*javaName.length()];
1024
// Convert to byte pairs
1025
int counter = 0;
1026
for (int i = 0; i < javaName.length();i++) {
1027
int ch = javaName.charAt(i);
1028
javaNameArray[counter++] = (byte)(ch >>> 8);
1029
javaNameArray[counter++] = (byte)ch;
1030
}
1031
1032
return stringToByteArray("/!" +
1033
Base64.byteArrayToAltBase64(javaNameArray));
1034
}
1035
1036
/**
1037
* Converts value string from its Windows representation
1038
* to java string. See
1039
* {@link #toWindowsValueString(String) toWindowsValueString()} for the
1040
* description of the encoding algorithm.
1041
*/
1042
private static String toJavaValueString(byte[] windowsNameArray) {
1043
String windowsName = byteArrayToString(windowsNameArray);
1044
StringBuilder javaName = new StringBuilder();
1045
char ch;
1046
for (int i = 0; i < windowsName.length(); i++){
1047
if ((ch = windowsName.charAt(i)) == '/') {
1048
char next = ' ';
1049
1050
if (windowsName.length() > i + 1 &&
1051
(next = windowsName.charAt(i + 1)) == 'u') {
1052
if (windowsName.length() < i + 6) {
1053
break;
1054
} else {
1055
ch = (char)Integer.parseInt(
1056
windowsName.substring(i + 2, i + 6), 16);
1057
i += 5;
1058
}
1059
} else
1060
if ((windowsName.length() > i + 1) &&
1061
((windowsName.charAt(i+1)) >= 'A') &&
1062
(next <= 'Z')) {
1063
ch = next;
1064
i++;
1065
} else if ((windowsName.length() > i + 1) &&
1066
(next == '/')) {
1067
ch = '\\';
1068
i++;
1069
}
1070
} else if (ch == '\\') {
1071
ch = '/';
1072
}
1073
javaName.append(ch);
1074
}
1075
return javaName.toString();
1076
}
1077
1078
/**
1079
* Converts value string to it Windows representation.
1080
* as a byte-encoded string.
1081
* Encoding algorithm adds "/" character to capital letters, i.e.
1082
* "A" is encoded as "/A". Character '\' is encoded as '//',
1083
* '/' is encoded as '\'.
1084
* Then convert java string to a byte array of ASCII characters.
1085
*/
1086
private static byte[] toWindowsValueString(String javaName) {
1087
StringBuilder windowsName = new StringBuilder();
1088
for (int i = 0; i < javaName.length(); i++) {
1089
char ch = javaName.charAt(i);
1090
if ((ch < 0x0020) || (ch > 0x007f)){
1091
// write \udddd
1092
windowsName.append("/u");
1093
String hex = Long.toHexString(javaName.charAt(i));
1094
StringBuilder hex4 = new StringBuilder(hex);
1095
hex4.reverse();
1096
int len = 4 - hex4.length();
1097
for (int j = 0; j < len; j++){
1098
hex4.append('0');
1099
}
1100
for (int j = 0; j < 4; j++){
1101
windowsName.append(hex4.charAt(3 - j));
1102
}
1103
} else if (ch == '\\') {
1104
windowsName.append("//");
1105
} else if (ch == '/') {
1106
windowsName.append('\\');
1107
} else if ((ch >= 'A') && (ch <='Z')) {
1108
windowsName.append('/').append(ch);
1109
} else {
1110
windowsName.append(ch);
1111
}
1112
}
1113
return stringToByteArray(windowsName.toString());
1114
}
1115
1116
/**
1117
* Returns native handle for the top Windows node for this node.
1118
*/
1119
private long rootNativeHandle() {
1120
return (isUserNode()
1121
? USER_ROOT_NATIVE_HANDLE
1122
: SYSTEM_ROOT_NATIVE_HANDLE);
1123
}
1124
1125
/**
1126
* Returns this java string as a null-terminated byte array
1127
*/
1128
private static byte[] stringToByteArray(String str) {
1129
byte[] result = new byte[str.length()+1];
1130
for (int i = 0; i < str.length(); i++) {
1131
result[i] = (byte) str.charAt(i);
1132
}
1133
result[str.length()] = 0;
1134
return result;
1135
}
1136
1137
/**
1138
* Converts a null-terminated byte array to java string
1139
*/
1140
private static String byteArrayToString(byte[] array) {
1141
StringBuilder result = new StringBuilder();
1142
for (int i = 0; i < array.length - 1; i++) {
1143
result.append((char)array[i]);
1144
}
1145
return result.toString();
1146
}
1147
1148
/**
1149
* Empty, never used implementation of AbstractPreferences.flushSpi().
1150
*/
1151
protected void flushSpi() throws BackingStoreException {
1152
// assert false;
1153
}
1154
1155
/**
1156
* Empty, never used implementation of AbstractPreferences.flushSpi().
1157
*/
1158
protected void syncSpi() throws BackingStoreException {
1159
// assert false;
1160
}
1161
1162
private static synchronized PlatformLogger logger() {
1163
if (logger == null) {
1164
logger = PlatformLogger.getLogger("java.util.prefs");
1165
}
1166
return logger;
1167
}
1168
}
1169
1170