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 }