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