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 " 408 + "node " + 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()) + " at root 0x" + 435 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 " 516 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 517 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 518 ". Windows RegOpenKey(...) returned error code " + 519 result[ERROR_CODE] + "."); 520 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; 521 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) { 522 throw new SecurityException("Could not open windows " 523 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 524 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 525 ": Access denied"); 526 } 527 } 528 return result[NATIVE_HANDLE]; 529 } else { 530 return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2); 531 } 532 } 533 534 /** 535 * Opens Windows registry key at a given relative path 536 * with respect to a given Windows registry key. 537 * @param windowsAbsolutePath Windows relative path of the 538 * key as a byte-encoded string. 539 * @param nativeHandle handle to the base Windows key. 540 * @param mask1 Preferred Windows security mask. 541 * @param mask2 Alternate Windows security mask. 542 * @return Windows registry key's handle. 543 * @see #openKey(int) 544 * @see #openKey(byte[],int) 545 * @see #closeKey(int) 546 */ 547 private int openKey(int nativeHandle, byte[] windowsRelativePath, 548 int mask1, int mask2) { 549 /* If the path is short enough open at once. Otherwise split the path */ 550 if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) { 551 int[] result = WindowsRegOpenKey1(nativeHandle, 552 windowsRelativePath, mask1); 553 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1) 554 result = WindowsRegOpenKey1(nativeHandle, 555 windowsRelativePath, mask2); 556 557 if (result[ERROR_CODE] != ERROR_SUCCESS) { 558 logger().warning("Could not open windows " 559 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 560 " at root 0x" + Integer.toHexString(nativeHandle) + 561 ". Windows RegOpenKey(...) returned error code " + 562 result[ERROR_CODE] + "."); 563 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE; 564 } 565 return result[NATIVE_HANDLE]; 566 } else { 567 int separatorPosition = -1; 568 // Be greedy - open the longest possible path 569 for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) { 570 if (windowsRelativePath[i] == ((byte)'\\')) { 571 separatorPosition = i; 572 break; 573 } 574 } 575 // Split the path and do the recursion 576 byte[] nextRelativeRoot = new byte[separatorPosition+1]; 577 System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0, 578 separatorPosition); 579 nextRelativeRoot[separatorPosition] = 0; 580 byte[] nextRelativePath = new byte[windowsRelativePath.length - 581 separatorPosition - 1]; 582 System.arraycopy(windowsRelativePath, separatorPosition+1, 583 nextRelativePath, 0, nextRelativePath.length); 584 int nextNativeHandle = openKey(nativeHandle, nextRelativeRoot, 585 mask1, mask2); 586 if (nextNativeHandle == NULL_NATIVE_HANDLE) { 587 return NULL_NATIVE_HANDLE; 588 } 589 int result = openKey(nextNativeHandle, nextRelativePath, 590 mask1,mask2); 591 closeKey(nextNativeHandle); 592 return result; 593 } 594 } 595 596 /** 597 * Closes Windows registry key. 598 * Logs a warning if Windows registry is unavailable. 599 * @param key's Windows registry handle. 600 * @see #openKey(int) 601 * @see #openKey(byte[],int) 602 * @see #openKey(int, byte[],int) 603 */ 604 private void closeKey(int nativeHandle) { 605 int result = WindowsRegCloseKey(nativeHandle); 606 if (result != ERROR_SUCCESS) { 607 logger().warning("Could not close windows " 608 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 609 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 610 ". Windows RegCloseKey(...) returned error code " + result + "."); 611 } 612 } 613 614 /** 615 * Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method. 616 * Puts name-value pair into the underlying Windows registry node. 617 * Logs a warning, if Windows registry is unavailable. 618 * @see #getSpi(String) 619 */ 620 protected void putSpi(String javaName, String value) { 621 int nativeHandle = openKey(KEY_SET_VALUE); 622 if (nativeHandle == NULL_NATIVE_HANDLE) { 623 isBackingStoreAvailable = false; 624 return; 625 } 626 int result = WindowsRegSetValueEx1(nativeHandle, 627 toWindowsName(javaName), toWindowsValueString(value)); 628 if (result != ERROR_SUCCESS) { 629 logger().warning("Could not assign value to key " + 630 byteArrayToString(toWindowsName(javaName))+ " at Windows registry node " 631 + byteArrayToString(windowsAbsolutePath()) + " at root 0x" 632 + Integer.toHexString(rootNativeHandle()) + 633 ". Windows RegSetValueEx(...) returned error code " + result + "."); 634 isBackingStoreAvailable = false; 635 } 636 closeKey(nativeHandle); 637 } 638 639 /** 640 * Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method. 641 * Gets a string value from the underlying Windows registry node. 642 * Logs a warning, if Windows registry is unavailable. 643 * @see #putSpi(String, String) 644 */ 645 protected String getSpi(String javaName) { 646 int nativeHandle = openKey(KEY_QUERY_VALUE); 647 if (nativeHandle == NULL_NATIVE_HANDLE) { 648 return null; 649 } 650 Object resultObject = WindowsRegQueryValueEx(nativeHandle, 651 toWindowsName(javaName)); 652 if (resultObject == null) { 653 closeKey(nativeHandle); 654 return null; 655 } 656 closeKey(nativeHandle); 657 return toJavaValueString((byte[]) resultObject); 658 } 659 660 /** 661 * Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method. 662 * Deletes a string name-value pair from the underlying Windows registry 663 * node, if this value still exists. 664 * Logs a warning, if Windows registry is unavailable or key has already 665 * been deleted. 666 */ 667 protected void removeSpi(String key) { 668 int nativeHandle = openKey(KEY_SET_VALUE); 669 if (nativeHandle == NULL_NATIVE_HANDLE) { 670 return; 671 } 672 int result = 673 WindowsRegDeleteValue(nativeHandle, toWindowsName(key)); 674 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { 675 logger().warning("Could not delete windows registry " 676 + "value " + byteArrayToString(windowsAbsolutePath())+ "\\" + 677 toWindowsName(key) + " at root 0x" + 678 Integer.toHexString(rootNativeHandle()) + 679 ". Windows RegDeleteValue(...) returned error code " + 680 result + "."); 681 isBackingStoreAvailable = false; 682 } 683 closeKey(nativeHandle); 684 } 685 686 /** 687 * Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method. 688 * Gets value names from the underlying Windows registry node. 689 * Throws a BackingStoreException and logs a warning, if 690 * Windows registry is unavailable. 691 */ 692 protected String[] keysSpi() throws BackingStoreException{ 693 // Find out the number of values 694 int nativeHandle = openKey(KEY_QUERY_VALUE); 695 if (nativeHandle == NULL_NATIVE_HANDLE) { 696 throw new BackingStoreException("Could not open windows" 697 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 698 " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); 699 } 700 int[] result = WindowsRegQueryInfoKey1(nativeHandle); 701 if (result[ERROR_CODE] != ERROR_SUCCESS) { 702 String info = "Could not query windows" 703 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 704 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 705 ". Windows RegQueryInfoKeyEx(...) returned error code " + 706 result[ERROR_CODE] + "."; 707 logger().warning(info); 708 throw new BackingStoreException(info); 709 } 710 int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH]; 711 int valuesNumber = result[VALUES_NUMBER]; 712 if (valuesNumber == 0) { 713 closeKey(nativeHandle); 714 return new String[0]; 715 } 716 // Get the values 717 String[] valueNames = new String[valuesNumber]; 718 for (int i = 0; i < valuesNumber; i++) { 719 byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i, 720 maxValueNameLength+1); 721 if (windowsName == null) { 722 String info = 723 "Could not enumerate value #" + i + " of windows node " + 724 byteArrayToString(windowsAbsolutePath()) + " at root 0x" + 725 Integer.toHexString(rootNativeHandle()) + "."; 726 logger().warning(info); 727 throw new BackingStoreException(info); 728 } 729 valueNames[i] = toJavaName(windowsName); 730 } 731 closeKey(nativeHandle); 732 return valueNames; 733 } 734 735 /** 736 * Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method. 737 * Calls Windows registry to retrive children of this node. 738 * Throws a BackingStoreException and logs a warning message, 739 * if Windows registry is not available. 740 */ 741 protected String[] childrenNamesSpi() throws BackingStoreException { 742 // Open key 743 int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS| KEY_QUERY_VALUE); 744 if (nativeHandle == NULL_NATIVE_HANDLE) { 745 throw new BackingStoreException("Could not open windows" 746 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 747 " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); 748 } 749 // Get number of children 750 int[] result = WindowsRegQueryInfoKey1(nativeHandle); 751 if (result[ERROR_CODE] != ERROR_SUCCESS) { 752 String info = "Could not query windows" 753 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 754 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 755 ". Windows RegQueryInfoKeyEx(...) returned error code " + 756 result[ERROR_CODE] + "."; 757 logger().warning(info); 758 throw new BackingStoreException(info); 759 } 760 int maxKeyLength = result[MAX_KEY_LENGTH]; 761 int subKeysNumber = result[SUBKEYS_NUMBER]; 762 if (subKeysNumber == 0) { 763 closeKey(nativeHandle); 764 return new String[0]; 765 } 766 String[] subkeys = new String[subKeysNumber]; 767 String[] children = new String[subKeysNumber]; 768 // Get children 769 for (int i = 0; i < subKeysNumber; i++) { 770 byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i, 771 maxKeyLength+1); 772 if (windowsName == null) { 773 String info = 774 "Could not enumerate key #" + i + " of windows node " + 775 byteArrayToString(windowsAbsolutePath()) + " at root 0x" + 776 Integer.toHexString(rootNativeHandle()) + ". "; 777 logger().warning(info); 778 throw new BackingStoreException(info); 779 } 780 String javaName = toJavaName(windowsName); 781 children[i] = javaName; 782 } 783 closeKey(nativeHandle); 784 return children; 785 } 786 787 /** 788 * Implements <tt>Preferences</tt> <tt>flush()</tt> method. 789 * Flushes Windows registry changes to disk. 790 * Throws a BackingStoreException and logs a warning message if Windows 791 * registry is not available. 792 */ 793 public void flush() throws BackingStoreException{ 794 795 if (isRemoved()) { 796 parent.flush(); 797 return; 798 } 799 if (!isBackingStoreAvailable) { 800 throw new BackingStoreException( 801 "flush(): Backing store not available."); 802 } 803 int nativeHandle = openKey(KEY_READ); 804 if (nativeHandle == NULL_NATIVE_HANDLE) { 805 throw new BackingStoreException("Could not open windows" 806 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 807 " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); 808 } 809 int result = WindowsRegFlushKey1(nativeHandle); 810 if (result != ERROR_SUCCESS) { 811 String info = "Could not flush windows " 812 + "registry node " + byteArrayToString(windowsAbsolutePath()) 813 + " at root 0x" + Integer.toHexString(rootNativeHandle()) + 814 ". Windows RegFlushKey(...) returned error code " + result + "."; 815 logger().warning(info); 816 throw new BackingStoreException(info); 817 } 818 closeKey(nativeHandle); 819 } 820 821 822 /** 823 * Implements <tt>Preferences</tt> <tt>sync()</tt> method. 824 * Flushes Windows registry changes to disk. Equivalent to flush(). 825 * @see flush() 826 */ 827 public void sync() throws BackingStoreException{ 828 if (isRemoved()) 829 throw new IllegalStateException("Node has been removed"); 830 flush(); 831 } 832 833 /** 834 * Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method. 835 * Constructs a child node with a 836 * given name and creates its underlying Windows registry node, 837 * if it does not exist. 838 * Logs a warning message, if Windows Registry is unavailable. 839 */ 840 protected AbstractPreferences childSpi(String name) { 841 return new WindowsPreferences(this, name); 842 } 843 844 /** 845 * Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method. 846 * Deletes underlying Windows registry node. 847 * Throws a BackingStoreException and logs a warning, if Windows registry 848 * is not available. 849 */ 850 public void removeNodeSpi() throws BackingStoreException { 851 int parentNativeHandle = 852 ((WindowsPreferences)parent()).openKey(DELETE); 853 if (parentNativeHandle == NULL_NATIVE_HANDLE) { 854 throw new BackingStoreException("Could not open parent windows" 855 + "registry node of " + byteArrayToString(windowsAbsolutePath()) + 856 " at root 0x" + Integer.toHexString(rootNativeHandle()) + "."); 857 } 858 int result = 859 WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name())); 860 if (result != ERROR_SUCCESS) { 861 String info = "Could not delete windows " 862 + "registry node " + byteArrayToString(windowsAbsolutePath()) + 863 " at root 0x" + Integer.toHexString(rootNativeHandle()) + 864 ". Windows RegDeleteKeyEx(...) returned error code " + 865 result + "."; 866 logger().warning(info); 867 throw new BackingStoreException(info); 868 } 869 closeKey(parentNativeHandle); 870 } 871 872 /** 873 * Converts value's or node's name from its byte array representation to 874 * java string. Two encodings, simple and altBase64 are used. See 875 * {@link #toWindowsName(String) toWindowsName()} for a detailed 876 * description of encoding conventions. 877 * @param windowsNameArray Null-terminated byte array. 878 */ 879 private static String toJavaName(byte[] windowsNameArray) { 880 String windowsName = byteArrayToString(windowsNameArray); 881 // check if Alt64 882 if ((windowsName.length()>1) && 883 (windowsName.substring(0,2).equals("/!"))) { 884 return toJavaAlt64Name(windowsName); 885 } 886 StringBuffer javaName = new StringBuffer(); 887 char ch; 888 // Decode from simple encoding 889 for (int i = 0; i < windowsName.length(); i++){ 890 if ((ch = windowsName.charAt(i)) == '/') { 891 char next = ' '; 892 if ((windowsName.length() > i + 1) && 893 ((next = windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) { 894 ch = next; 895 i++; 896 } else if ((windowsName.length() > i + 1) && (next == '/')) { 897 ch = '\\'; 898 i++; 899 } 900 } else if (ch == '\\') { 901 ch = '/'; 902 } 903 javaName.append(ch); 904 } 905 return javaName.toString(); 906 } 907 908 /** 909 * Converts value's or node's name from its Windows representation to java 910 * string, using altBase64 encoding. See 911 * {@link #toWindowsName(String) toWindowsName()} for a detailed 912 * description of encoding conventions. 913 */ 914 915 private static String toJavaAlt64Name(String windowsName) { 916 byte[] byteBuffer = 917 Base64.altBase64ToByteArray(windowsName.substring(2)); 918 StringBuffer result = new StringBuffer(); 919 for (int i = 0; i < byteBuffer.length; i++) { 920 int firstbyte = (byteBuffer[i++] & 0xff); 921 int secondbyte = (byteBuffer[i] & 0xff); 922 result.append((char)((firstbyte << 8) + secondbyte)); 923 } 924 return result.toString(); 925 } 926 927 /** 928 * Converts value's or node's name to its Windows representation 929 * as a byte-encoded string. 930 * Two encodings, simple and altBase64 are used. 931 * <p> 932 * <i>Simple</i> encoding is used, if java string does not contain 933 * any characters less, than 0x0020, or greater, than 0x007f. 934 * Simple encoding adds "/" character to capital letters, i.e. 935 * "A" is encoded as "/A". Character '\' is encoded as '//', 936 * '/' is encoded as '\'. 937 * The constructed string is converted to byte array by truncating the 938 * highest byte and adding the terminating <tt>null</tt> character. 939 * <p> 940 * <i>altBase64</i> encoding is used, if java string does contain at least 941 * one character less, than 0x0020, or greater, than 0x007f. 942 * This encoding is marked by setting first two bytes of the 943 * Windows string to '/!'. The java name is then encoded using 944 * byteArrayToAltBase64() method from 945 * Base64 class. 946 */ 947 private static byte[] toWindowsName(String javaName) { 948 StringBuffer windowsName = new StringBuffer(); 949 for (int i = 0; i < javaName.length(); i++) { 950 char ch =javaName.charAt(i); 951 if ((ch < 0x0020)||(ch > 0x007f)) { 952 // If a non-trivial character encountered, use altBase64 953 return toWindowsAlt64Name(javaName); 954 } 955 if (ch == '\\') { 956 windowsName.append("//"); 957 } else if (ch == '/') { 958 windowsName.append('\\'); 959 } else if ((ch >= 'A') && (ch <='Z')) { 960 windowsName.append("/" + ch); 961 } else { 962 windowsName.append(ch); 963 } 964 } 965 return stringToByteArray(windowsName.toString()); 966 } 967 968 /** 969 * Converts value's or node's name to its Windows representation 970 * as a byte-encoded string, using altBase64 encoding. See 971 * {@link #toWindowsName(String) toWindowsName()} for a detailed 972 * description of encoding conventions. 973 */ 974 private static byte[] toWindowsAlt64Name(String javaName) { 975 byte[] javaNameArray = new byte[2*javaName.length()]; 976 // Convert to byte pairs 977 int counter = 0; 978 for (int i = 0; i < javaName.length();i++) { 979 int ch = javaName.charAt(i); 980 javaNameArray[counter++] = (byte)(ch >>> 8); 981 javaNameArray[counter++] = (byte)ch; 982 } 983 984 return stringToByteArray( 985 "/!" + Base64.byteArrayToAltBase64(javaNameArray)); 986 } 987 988 /** 989 * Converts value string from its Windows representation 990 * to java string. See 991 * {@link #toWindowsValueString(String) toWindowsValueString()} for the 992 * description of the encoding algorithm. 993 */ 994 private static String toJavaValueString(byte[] windowsNameArray) { 995 // Use modified native2ascii algorithm 996 String windowsName = byteArrayToString(windowsNameArray); 997 StringBuffer javaName = new StringBuffer(); 998 char ch; 999 for (int i = 0; i < windowsName.length(); i++){ 1000 if ((ch = windowsName.charAt(i)) == '/') { 1001 char next = ' '; 1002 1003 if (windowsName.length() > i + 1 && 1004 (next = windowsName.charAt(i + 1)) == 'u') { 1005 if (windowsName.length() < i + 6){ 1006 break; 1007 } else { 1008 ch = (char)Integer.parseInt 1009 (windowsName.substring(i + 2, i + 6), 16); 1010 i += 5; 1011 } 1012 } else 1013 if ((windowsName.length() > i + 1) && 1014 ((windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) { 1015 ch = next; 1016 i++; 1017 } else if ((windowsName.length() > i + 1) && 1018 (next == '/')) { 1019 ch = '\\'; 1020 i++; 1021 } 1022 } else if (ch == '\\') { 1023 ch = '/'; 1024 } 1025 javaName.append(ch); 1026 } 1027 return javaName.toString(); 1028 } 1029 1030 /** 1031 * Converts value string to it Windows representation. 1032 * as a byte-encoded string. 1033 * Encoding algorithm adds "/" character to capital letters, i.e. 1034 * "A" is encoded as "/A". Character '\' is encoded as '//', 1035 * '/' is encoded as '\'. 1036 * Then encoding scheme similar to jdk's native2ascii converter is used 1037 * to convert java string to a byte array of ASCII characters. 1038 */ 1039 private static byte[] toWindowsValueString(String javaName) { 1040 StringBuffer windowsName = new StringBuffer(); 1041 for (int i = 0; i < javaName.length(); i++) { 1042 char ch =javaName.charAt(i); 1043 if ((ch < 0x0020)||(ch > 0x007f)){ 1044 // write \udddd 1045 windowsName.append("/u"); 1046 String hex = Integer.toHexString(javaName.charAt(i)); 1047 StringBuffer hex4 = new StringBuffer(hex); 1048 hex4.reverse(); 1049 int len = 4 - hex4.length(); 1050 for (int j = 0; j < len; j++){ 1051 hex4.append('0'); 1052 } 1053 for (int j = 0; j < 4; j++){ 1054 windowsName.append(hex4.charAt(3 - j)); 1055 } 1056 } else if (ch == '\\') { 1057 windowsName.append("//"); 1058 } else if (ch == '/') { 1059 windowsName.append('\\'); 1060 } else if ((ch >= 'A') && (ch <='Z')) { 1061 windowsName.append("/" + ch); 1062 } else { 1063 windowsName.append(ch); 1064 } 1065 } 1066 return stringToByteArray(windowsName.toString()); 1067 } 1068 1069 /** 1070 * Returns native handle for the top Windows node for this node. 1071 */ 1072 private int rootNativeHandle() { 1073 return (isUserNode()? USER_ROOT_NATIVE_HANDLE : 1074 SYSTEM_ROOT_NATIVE_HANDLE); 1075 } 1076 1077 /** 1078 * Returns this java string as a null-terminated byte array 1079 */ 1080 private static byte[] stringToByteArray(String str) { 1081 byte[] result = new byte[str.length()+1]; 1082 for (int i = 0; i < str.length(); i++) { 1083 result[i] = (byte) str.charAt(i); 1084 } 1085 result[str.length()] = 0; 1086 return result; 1087 } 1088 1089 /** 1090 * Converts a null-terminated byte array to java string 1091 */ 1092 private static String byteArrayToString(byte[] array) { 1093 StringBuffer result = new StringBuffer(); 1094 for (int i = 0; i < array.length - 1; i++) { 1095 result.append((char)array[i]); 1096 } 1097 return result.toString(); 1098 } 1099 1100 /** 1101 * Empty, never used implementation of AbstractPreferences.flushSpi(). 1102 */ 1103 protected void flushSpi() throws BackingStoreException { 1104 // assert false; 1105 } 1106 1107 /** 1108 * Empty, never used implementation of AbstractPreferences.flushSpi(). 1109 */ 1110 protected void syncSpi() throws BackingStoreException { 1111 // assert false; 1112 } 1113 1114 private static synchronized PlatformLogger logger() { 1115 if (logger == null) { 1116 logger = PlatformLogger.getLogger("java.util.prefs"); 1117 } 1118 return logger; 1119 } 1120 }