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