PageRenderTime 59ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/java-1.7.0-openjdk/openjdk/jdk/src/windows/classes/java/util/prefs/WindowsPreferences.java

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