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