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