KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > util > prefs > WindowsPreferences


1 /*
2  * @(#)WindowsPreferences.java 1.18 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.util.prefs;
9
10 import java.util.Map JavaDoc;
11 import java.util.TreeMap JavaDoc;
12 import java.util.StringTokenizer JavaDoc;
13 import java.io.ByteArrayOutputStream JavaDoc;
14 import java.util.logging.Logger JavaDoc;
15
16 /**
17  * Windows registry based implementation of <tt>Preferences</tt>.
18  * <tt>Preferences</tt>' <tt>systemRoot</tt> and <tt>userRoot</tt> are stored in
19  * <tt>HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs</tt> and
20  * <tt>HKEY_CURRENT_USER\Software\JavaSoft\Prefs</tt> correspondingly.
21  *
22  * @author Konstantin Kladko
23  * @version 1.18, 12/19/03
24  * @see Preferences
25  * @see PreferencesFactory
26  * @since 1.4
27  */

28
29 class WindowsPreferences extends AbstractPreferences JavaDoc{
30         
31     /**
32      * Logger for error messages
33      */

34     private static Logger JavaDoc logger;
35
36     /**
37      * Windows registry path to <tt>Preferences</tt>'s root nodes.
38      */

39     private static final byte[] WINDOWS_ROOT_PATH
40                                = stringToByteArray("Software\\JavaSoft\\Prefs");
41
42     /**
43      * Windows handles to <tt>HKEY_CURRENT_USER</tt> and
44      * <tt>HKEY_LOCAL_MACHINE</tt> hives.
45      */

46     private static final int HKEY_CURRENT_USER = 0x80000001;
47     private static final int HKEY_LOCAL_MACHINE = 0x80000002;
48     
49     /**
50      * Mount point for <tt>Preferences</tt>' user root.
51      */

52     private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER;
53     
54     /**
55      * Mount point for <tt>Preferences</tt>' system root.
56      */

57     private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE;
58     
59     /**
60      * Maximum byte-encoded path length for Windows native functions,
61      * ending <tt>null</tt> character not included.
62      */

63     private static final int MAX_WINDOWS_PATH_LENGTH = 256;
64     
65     /**
66      * User root node.
67      */

68     static final Preferences JavaDoc userRoot =
69          new WindowsPreferences JavaDoc(USER_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
70     
71     /**
72      * System root node.
73      */

74     static final Preferences JavaDoc systemRoot =
75         new WindowsPreferences JavaDoc(SYSTEM_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
76         
77     /* Windows error codes. */
78     private static final int ERROR_SUCCESS = 0;
79     private static final int ERROR_FILE_NOT_FOUND = 2;
80     private static final int ERROR_ACCESS_DENIED = 5;
81     
82     /* Constants used to interpret returns of native functions */
83     private static final int NATIVE_HANDLE = 0;
84     private static final int ERROR_CODE = 1;
85     private static final int SUBKEYS_NUMBER = 0;
86     private static final int VALUES_NUMBER = 2;
87     private static final int MAX_KEY_LENGTH = 3;
88     private static final int MAX_VALUE_NAME_LENGTH = 4;
89     private static final int DISPOSITION = 2;
90     private static final int REG_CREATED_NEW_KEY = 1;
91     private static final int REG_OPENED_EXISTING_KEY = 2;
92     private static final int NULL_NATIVE_HANDLE = 0;
93     
94     /* Windows security masks */
95     private static final int DELETE = 0x10000;
96     private static final int KEY_QUERY_VALUE = 1;
97     private static final int KEY_SET_VALUE = 2;
98     private static final int KEY_CREATE_SUB_KEY = 4;
99     private static final int KEY_ENUMERATE_SUB_KEYS = 8;
100     private static final int KEY_READ = 0x20019;
101     private static final int KEY_WRITE = 0x20006;
102     private static final int KEY_ALL_ACCESS = 0xf003f;
103
104     /**
105      * Initial time between registry access attempts, in ms. The time is doubled
106      * after each failing attempt (except the first).
107      */

108     private static int INIT_SLEEP_TIME = 50;
109
110     /**
111      * Maximum number of registry access attempts.
112      */

113     private static int MAX_ATTEMPTS = 5;
114
115     /**
116      * BackingStore availability flag.
117      */

118     private boolean isBackingStoreAvailable = true;
119     
120     /**
121      * Java wrapper for Windows registry API RegOpenKey()
122      */

123     private static native int[] WindowsRegOpenKey(int hKey, byte[] subKey,
124                                                          int securityMask);
125     /**
126      * Retries RegOpenKey() MAX_ATTEMPTS times before giving up.
127      */

128     private static int[] WindowsRegOpenKey1(int hKey, byte[] subKey,
129                                                       int securityMask) {
130         int[] result = WindowsRegOpenKey(hKey, subKey, securityMask);
131         if (result[ERROR_CODE] == ERROR_SUCCESS) {
132             return result;
133         } else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) {
134             logger().warning("Trying to recreate Windows registry node " +
135             byteArrayToString(subKey) + " at root 0x" +
136             Integer.toHexString(hKey) + ".");
137             // Try recreation
138
int handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE];
139             WindowsRegCloseKey(handle);
140             return WindowsRegOpenKey(hKey, subKey, securityMask);
141         } else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) {
142             long sleepTime = INIT_SLEEP_TIME;
143             for (int i = 0; i < MAX_ATTEMPTS; i++) {
144             try {
145                 Thread.sleep(sleepTime);
146             } catch(InterruptedException JavaDoc e) {
147                 return result;
148             }
149             sleepTime *= 2;
150             result = WindowsRegOpenKey(hKey, subKey, securityMask);
151             if (result[ERROR_CODE] == ERROR_SUCCESS) {
152                 return result;
153             }
154             }
155         }
156         return result;
157     }
158     
159      /**
160      * Java wrapper for Windows registry API RegCloseKey()
161      */

162     private static native int WindowsRegCloseKey(int hKey);
163     
164     /**
165      * Java wrapper for Windows registry API RegCreateKeyEx()
166      */

167     private static native int[] WindowsRegCreateKeyEx(int hKey, byte[] subKey);
168     
169     /**
170      * Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.
171      */

172     private static int[] WindowsRegCreateKeyEx1(int hKey, byte[] subKey) {
173         int[] result = WindowsRegCreateKeyEx(hKey, subKey);
174         if (result[ERROR_CODE] == ERROR_SUCCESS) {
175                 return result;
176             } else {
177                 long sleepTime = INIT_SLEEP_TIME;
178                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
179                 try {
180                     Thread.sleep(sleepTime);
181                 } catch(InterruptedException JavaDoc e) {
182                     return result;
183                 }
184                 sleepTime *= 2;
185                 result = WindowsRegCreateKeyEx(hKey, subKey);
186                 if (result[ERROR_CODE] == ERROR_SUCCESS) {
187                 return result;
188                 }
189             }
190         }
191         return result;
192     }
193     /**
194      * Java wrapper for Windows registry API RegDeleteKey()
195      */

196     private static native int WindowsRegDeleteKey(int hKey, byte[] subKey);
197     
198     /**
199      * Java wrapper for Windows registry API RegFlushKey()
200      */

201     private static native int WindowsRegFlushKey(int hKey);
202     
203     /**
204      * Retries RegFlushKey() MAX_ATTEMPTS times before giving up.
205      */

206     private static int WindowsRegFlushKey1(int hKey) {
207         int result = WindowsRegFlushKey(hKey);
208         if (result == ERROR_SUCCESS) {
209                 return result;
210             } else {
211                 long sleepTime = INIT_SLEEP_TIME;
212                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
213                 try {
214                     Thread.sleep(sleepTime);
215                 } catch(InterruptedException JavaDoc e) {
216                     return result;
217                 }
218                 sleepTime *= 2;
219                 result = WindowsRegFlushKey(hKey);
220                 if (result == ERROR_SUCCESS) {
221                 return result;
222                 }
223             }
224         }
225         return result;
226     }
227
228     /**
229      * Java wrapper for Windows registry API RegQueryValueEx()
230      */

231     private static native byte[] WindowsRegQueryValueEx(int hKey,
232                                                               byte[] valueName);
233     /**
234      * Java wrapper for Windows registry API RegSetValueEx()
235      */

236     private static native int WindowsRegSetValueEx(int hKey, byte[] valueName,
237                                                          byte[] value);
238     /**
239      * Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.
240      */

241     private static int WindowsRegSetValueEx1(int hKey, byte[] valueName,
242                                                          byte[] value) {
243         int result = WindowsRegSetValueEx(hKey, valueName, value);
244         if (result == ERROR_SUCCESS) {
245                 return result;
246             } else {
247                 long sleepTime = INIT_SLEEP_TIME;
248                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
249                 try {
250                     Thread.sleep(sleepTime);
251                 } catch(InterruptedException JavaDoc e) {
252                     return result;
253                 }
254                 sleepTime *= 2;
255                 result = WindowsRegSetValueEx(hKey, valueName, value);
256                 if (result == ERROR_SUCCESS) {
257                 return result;
258                 }
259             }
260         }
261         return result;
262     }
263
264     /**
265      * Java wrapper for Windows registry API RegDeleteValue()
266      */

267     private static native int WindowsRegDeleteValue(int hKey, byte[] valueName);
268     
269     /**
270      * Java wrapper for Windows registry API RegQueryInfoKey()
271      */

272     private static native int[] WindowsRegQueryInfoKey(int hKey);
273     
274     /**
275      * Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.
276      */

277     private static int[] WindowsRegQueryInfoKey1(int hKey) {
278         int[] result = WindowsRegQueryInfoKey(hKey);
279         if (result[ERROR_CODE] == ERROR_SUCCESS) {
280                 return result;
281             } else {
282                 long sleepTime = INIT_SLEEP_TIME;
283                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
284                 try {
285                     Thread.sleep(sleepTime);
286                 } catch(InterruptedException JavaDoc e) {
287                     return result;
288                 }
289                 sleepTime *= 2;
290                 result = WindowsRegQueryInfoKey(hKey);
291                 if (result[ERROR_CODE] == ERROR_SUCCESS) {
292                 return result;
293                 }
294             }
295         }
296         return result;
297     }
298
299     /**
300      * Java wrapper for Windows registry API RegEnumKeyEx()
301      */

302     private static native byte[] WindowsRegEnumKeyEx(int hKey, int subKeyIndex,
303                                       int maxKeyLength);
304
305     /**
306      * Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up.
307      */

308     private static byte[] WindowsRegEnumKeyEx1(int hKey, int subKeyIndex,
309                                       int maxKeyLength) {
310         byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
311         if (result != null) {
312                 return result;
313             } else {
314                 long sleepTime = INIT_SLEEP_TIME;
315                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
316                 try {
317                     Thread.sleep(sleepTime);
318                 } catch(InterruptedException JavaDoc e) {
319                     return result;
320                 }
321                 sleepTime *= 2;
322                 result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
323                 if (result != null) {
324                 return result;
325                 }
326             }
327         }
328         return result;
329     }
330     
331     /**
332      * Java wrapper for Windows registry API RegEnumValue()
333      */

334     private static native byte[] WindowsRegEnumValue(int hKey, int valueIndex,
335                                       int maxValueNameLength);
336     /**
337      * Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.
338      */

339     private static byte[] WindowsRegEnumValue1(int hKey, int valueIndex,
340                                       int maxValueNameLength) {
341         byte[] result = WindowsRegEnumValue(hKey, valueIndex,
342                                                             maxValueNameLength);
343         if (result != null) {
344                 return result;
345             } else {
346                 long sleepTime = INIT_SLEEP_TIME;
347                 for (int i = 0; i < MAX_ATTEMPTS; i++) {
348                 try {
349                     Thread.sleep(sleepTime);
350                 } catch(InterruptedException JavaDoc e) {
351                     return result;
352                 }
353                 sleepTime *= 2;
354                 result = WindowsRegEnumValue(hKey, valueIndex,
355                                                             maxValueNameLength);
356                 if (result != null) {
357                 return result;
358                 }
359             }
360         }
361         return result;
362     }
363     
364     /**
365      * Constructs a <tt>WindowsPreferences</tt> node, creating underlying
366      * Windows registry node and all its Windows parents, if they are not yet
367      * created.
368      * Logs a warning message, if Windows Registry is unavailable.
369      */

370     private WindowsPreferences(WindowsPreferences JavaDoc parent, String JavaDoc name) {
371         super(parent, name);
372         int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ);
373         if (parentNativeHandle == NULL_NATIVE_HANDLE) {
374             // if here, openKey failed and logged
375
isBackingStoreAvailable = false;
376             return;
377         }
378         int[] result =
379                WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name));
380         if (result[ERROR_CODE] != ERROR_SUCCESS) {
381             logger().warning("Could not create windows registry "
382             + "node " + byteArrayToString(windowsAbsolutePath()) +
383             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
384             ". Windows RegCreateKeyEx(...) returned error code " +
385             result[ERROR_CODE] + ".");
386             isBackingStoreAvailable = false;
387             return;
388         }
389         newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
390         closeKey(parentNativeHandle);
391         closeKey(result[NATIVE_HANDLE]);
392     }
393
394     /**
395      * Constructs a root node creating the underlying
396      * Windows registry node and all of its parents, if they have not yet been
397      * created.
398      * Logs a warning message, if Windows Registry is unavailable.
399      * @param rootNativeHandle Native handle to one of Windows top level keys.
400      * @param rootDirectory Path to root directory, as a byte-encoded string.
401      */

402     private WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) {
403         super(null,"");
404         int[] result =
405                 WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory);
406         if (result[ERROR_CODE] != ERROR_SUCCESS) {
407             logger().warning("Could not open/create prefs root node " +
408             byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
409             Integer.toHexString(rootNativeHandle()) +
410             ". Windows RegCreateKeyEx(...) returned error code " +
411             result[ERROR_CODE] + ".");
412             isBackingStoreAvailable = false;
413             return;
414         }
415         // Check if a new node
416
newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
417         closeKey(result[NATIVE_HANDLE]);
418     }
419
420     /**
421      * Returns Windows absolute path of the current node as a byte array.
422      * Java "/" separator is transformed into Windows "\".
423      * @see Preferences#absolutePath()
424      */

425     private byte[] windowsAbsolutePath() {
426         ByteArrayOutputStream JavaDoc bstream = new ByteArrayOutputStream JavaDoc();
427         bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);
428         StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(absolutePath(),"/");
429         while (tokenizer.hasMoreTokens()) {
430             bstream.write((byte)'\\');
431             String JavaDoc nextName = tokenizer.nextToken();
432             byte[] windowsNextName = toWindowsName(nextName);
433             bstream.write(windowsNextName, 0, windowsNextName.length-1);
434         }
435         bstream.write(0);
436         return bstream.toByteArray();
437     }
438
439     /**
440      * Opens current node's underlying Windows registry key using a
441      * given security mask.
442      * @param securityMask Windows security mask.
443      * @return Windows registry key's handle.
444      * @see #openKey(byte[], int)
445      * @see #openKey(int, byte[], int)
446      * @see #closeKey(int)
447      */

448     private int openKey(int securityMask) {
449         return openKey(securityMask, securityMask);
450     }
451
452     /**
453      * Opens current node's underlying Windows registry key using a
454      * given security mask.
455      * @param mask1 Preferred Windows security mask.
456      * @param mask2 Alternate Windows security mask.
457      * @return Windows registry key's handle.
458      * @see #openKey(byte[], int)
459      * @see #openKey(int, byte[], int)
460      * @see #closeKey(int)
461      */

462     private int openKey(int mask1, int mask2) {
463         return openKey(windowsAbsolutePath(), mask1, mask2);
464     }
465
466      /**
467      * Opens Windows registry key at a given absolute path using a given
468      * security mask.
469      * @param windowsAbsolutePath Windows absolute path of the
470      * key as a byte-encoded string.
471      * @param mask1 Preferred Windows security mask.
472      * @param mask2 Alternate Windows security mask.
473      * @return Windows registry key's handle.
474      * @see #openKey(int)
475      * @see #openKey(int, byte[],int)
476      * @see #closeKey(int)
477      */

478     private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {
479         /* Check if key's path is short enough be opened at once
480             otherwise use a path-splitting procedure */

481         if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {
482              int[] result = WindowsRegOpenKey1(rootNativeHandle(),
483                                                windowsAbsolutePath, mask1);
484              if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
485                  result = WindowsRegOpenKey1(rootNativeHandle(),
486                                              windowsAbsolutePath, mask2);
487
488              if (result[ERROR_CODE] != ERROR_SUCCESS) {
489                 logger().warning("Could not open windows "
490                 + "registry node " + byteArrayToString(windowsAbsolutePath()) +
491                 " at root 0x" + Integer.toHexString(rootNativeHandle()) +
492                 ". Windows RegOpenKey(...) returned error code " +
493                 result[ERROR_CODE] + ".");
494                 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
495                 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {
496                     throw new SecurityException JavaDoc("Could not open windows "
497                 + "registry node " + byteArrayToString(windowsAbsolutePath()) +
498                 " at root 0x" + Integer.toHexString(rootNativeHandle()) +
499                 ": Access denied");
500                 }
501              }
502              return result[NATIVE_HANDLE];
503         } else {
504             return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);
505         }
506     }
507     
508      /**
509      * Opens Windows registry key at a given relative path
510      * with respect to a given Windows registry key.
511      * @param windowsAbsolutePath Windows relative path of the
512      * key as a byte-encoded string.
513      * @param nativeHandle handle to the base Windows key.
514      * @param mask1 Preferred Windows security mask.
515      * @param mask2 Alternate Windows security mask.
516      * @return Windows registry key's handle.
517      * @see #openKey(int)
518      * @see #openKey(byte[],int)
519      * @see #closeKey(int)
520      */

521     private int openKey(int nativeHandle, byte[] windowsRelativePath,
522                         int mask1, int mask2) {
523     /* If the path is short enough open at once. Otherwise split the path */
524         if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) {
525              int[] result = WindowsRegOpenKey1(nativeHandle,
526                                                windowsRelativePath, mask1);
527              if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
528                  result = WindowsRegOpenKey1(nativeHandle,
529                                              windowsRelativePath, mask2);
530
531              if (result[ERROR_CODE] != ERROR_SUCCESS) {
532                 logger().warning("Could not open windows "
533                 + "registry node " + byteArrayToString(windowsAbsolutePath()) +
534                 " at root 0x" + Integer.toHexString(nativeHandle) +
535                 ". Windows RegOpenKey(...) returned error code " +
536                 result[ERROR_CODE] + ".");
537                 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
538              }
539              return result[NATIVE_HANDLE];
540         } else {
541             int separatorPosition = -1;
542             // Be greedy - open the longest possible path
543
for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {
544                 if (windowsRelativePath[i] == ((byte)'\\')) {
545                     separatorPosition = i;
546                     break;
547                 }
548             }
549             // Split the path and do the recursion
550
byte[] nextRelativeRoot = new byte[separatorPosition+1];
551             System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0,
552                                                       separatorPosition);
553             nextRelativeRoot[separatorPosition] = 0;
554             byte[] nextRelativePath = new byte[windowsRelativePath.length -
555                                       separatorPosition - 1];
556             System.arraycopy(windowsRelativePath, separatorPosition+1,
557                              nextRelativePath, 0, nextRelativePath.length);
558             int nextNativeHandle = openKey(nativeHandle, nextRelativeRoot,
559                                            mask1, mask2);
560             if (nextNativeHandle == NULL_NATIVE_HANDLE) {
561                 return NULL_NATIVE_HANDLE;
562             }
563             int result = openKey(nextNativeHandle, nextRelativePath,
564                                  mask1,mask2);
565             closeKey(nextNativeHandle);
566             return result;
567         }
568     }
569
570      /**
571      * Closes Windows registry key.
572      * Logs a warning if Windows registry is unavailable.
573      * @param key's Windows registry handle.
574      * @see #openKey(int)
575      * @see #openKey(byte[],int)
576      * @see #openKey(int, byte[],int)
577     */

578     private void closeKey(int nativeHandle) {
579         int result = WindowsRegCloseKey(nativeHandle);
580         if (result != ERROR_SUCCESS) {
581             logger().warning("Could not close windows "
582             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
583             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
584             ". Windows RegCloseKey(...) returned error code " + result + ".");
585         }
586     }
587     
588      /**
589      * Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method.
590      * Puts name-value pair into the underlying Windows registry node.
591      * Logs a warning, if Windows registry is unavailable.
592      * @see #getSpi(String)
593      */

594     protected void putSpi(String JavaDoc javaName, String JavaDoc value) {
595     int nativeHandle = openKey(KEY_SET_VALUE);
596     if (nativeHandle == NULL_NATIVE_HANDLE) {
597         isBackingStoreAvailable = false;
598         return;
599     }
600     int result = WindowsRegSetValueEx1(nativeHandle,
601                           toWindowsName(javaName), toWindowsValueString(value));
602     if (result != ERROR_SUCCESS) {
603         logger().warning("Could not assign value to key " +
604         byteArrayToString(toWindowsName(javaName))+ " at Windows registry node "
605        + byteArrayToString(windowsAbsolutePath()) + " at root 0x"
606        + Integer.toHexString(rootNativeHandle()) +
607        ". Windows RegSetValueEx(...) returned error code " + result + ".");
608         isBackingStoreAvailable = false;
609         }
610     closeKey(nativeHandle);
611     }
612     
613     /**
614      * Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method.
615      * Gets a string value from the underlying Windows registry node.
616      * Logs a warning, if Windows registry is unavailable.
617      * @see #putSpi(String, String)
618      */

619     protected String JavaDoc getSpi(String JavaDoc javaName) {
620     int nativeHandle = openKey(KEY_QUERY_VALUE);
621     if (nativeHandle == NULL_NATIVE_HANDLE) {
622         return null;
623     }
624     Object JavaDoc resultObject = WindowsRegQueryValueEx(nativeHandle,
625                                                   toWindowsName(javaName));
626     if (resultObject == null) {
627         closeKey(nativeHandle);
628         return null;
629     }
630     closeKey(nativeHandle);
631     return toJavaValueString((byte[]) resultObject);
632     }
633
634     /**
635      * Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method.
636      * Deletes a string name-value pair from the underlying Windows registry
637      * node, if this value still exists.
638      * Logs a warning, if Windows registry is unavailable or key has already
639      * been deleted.
640      */

641     protected void removeSpi(String JavaDoc key) {
642         int nativeHandle = openKey(KEY_SET_VALUE);
643         if (nativeHandle == NULL_NATIVE_HANDLE) {
644         return;
645         }
646         int result =
647             WindowsRegDeleteValue(nativeHandle, toWindowsName(key));
648         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
649             logger().warning("Could not delete windows registry "
650             + "value " + byteArrayToString(windowsAbsolutePath())+ "\\" +
651             toWindowsName(key) + " at root 0x" +
652             Integer.toHexString(rootNativeHandle()) +
653             ". Windows RegDeleteValue(...) returned error code " +
654             result + ".");
655             isBackingStoreAvailable = false;
656         }
657         closeKey(nativeHandle);
658     }
659
660     /**
661      * Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method.
662      * Gets value names from the underlying Windows registry node.
663      * Throws a BackingStoreException and logs a warning, if
664      * Windows registry is unavailable.
665      */

666     protected String JavaDoc[] keysSpi() throws BackingStoreException JavaDoc{
667         // Find out the number of values
668
int nativeHandle = openKey(KEY_QUERY_VALUE);
669         if (nativeHandle == NULL_NATIVE_HANDLE) {
670             throw new BackingStoreException JavaDoc("Could not open windows"
671             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
672             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");
673         }
674         int[] result = WindowsRegQueryInfoKey1(nativeHandle);
675         if (result[ERROR_CODE] != ERROR_SUCCESS) {
676             String JavaDoc info = "Could not query windows"
677             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
678             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
679             ". Windows RegQueryInfoKeyEx(...) returned error code " +
680             result[ERROR_CODE] + ".";
681             logger().warning(info);
682             throw new BackingStoreException JavaDoc(info);
683         }
684         int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH];
685         int valuesNumber = result[VALUES_NUMBER];
686         if (valuesNumber == 0) {
687             closeKey(nativeHandle);
688             return new String JavaDoc[0];
689        }
690        // Get the values
691
String JavaDoc[] valueNames = new String JavaDoc[valuesNumber];
692        for (int i = 0; i < valuesNumber; i++) {
693             byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,
694                                                         maxValueNameLength+1);
695             if (windowsName == null) {
696                 String JavaDoc info =
697                 "Could not enumerate value #" + i + " of windows node " +
698                 byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
699                 Integer.toHexString(rootNativeHandle()) + ".";
700                 logger().warning(info);
701                 throw new BackingStoreException JavaDoc(info);
702             }
703             valueNames[i] = toJavaName(windowsName);
704         }
705         closeKey(nativeHandle);
706         return valueNames;
707     }
708
709     /**
710      * Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method.
711      * Calls Windows registry to retrive children of this node.
712      * Throws a BackingStoreException and logs a warning message,
713      * if Windows registry is not available.
714      */

715     protected String JavaDoc[] childrenNamesSpi() throws BackingStoreException JavaDoc {
716         // Open key
717
int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS| KEY_QUERY_VALUE);
718         if (nativeHandle == NULL_NATIVE_HANDLE) {
719             throw new BackingStoreException JavaDoc("Could not open windows"
720             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
721             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");
722         }
723         // Get number of children
724
int[] result = WindowsRegQueryInfoKey1(nativeHandle);
725         if (result[ERROR_CODE] != ERROR_SUCCESS) {
726             String JavaDoc info = "Could not query windows"
727             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
728             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
729             ". Windows RegQueryInfoKeyEx(...) returned error code " +
730             result[ERROR_CODE] + ".";
731             logger().warning(info);
732             throw new BackingStoreException JavaDoc(info);
733         }
734         int maxKeyLength = result[MAX_KEY_LENGTH];
735         int subKeysNumber = result[SUBKEYS_NUMBER];
736         if (subKeysNumber == 0) {
737             closeKey(nativeHandle);
738             return new String JavaDoc[0];
739         }
740         String JavaDoc[] subkeys = new String JavaDoc[subKeysNumber];
741         String JavaDoc[] children = new String JavaDoc[subKeysNumber];
742         // Get children
743
for (int i = 0; i < subKeysNumber; i++) {
744             byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,
745                                                                 maxKeyLength+1);
746             if (windowsName == null) {
747                 String JavaDoc info =
748                 "Could not enumerate key #" + i + " of windows node " +
749                 byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
750                 Integer.toHexString(rootNativeHandle()) + ". ";
751                 logger().warning(info);
752                 throw new BackingStoreException JavaDoc(info);
753             }
754             String JavaDoc javaName = toJavaName(windowsName);
755             children[i] = javaName;
756         }
757         closeKey(nativeHandle);
758         return children;
759     }
760
761     /**
762      * Implements <tt>Preferences</tt> <tt>flush()</tt> method.
763      * Flushes Windows registry changes to disk.
764      * Throws a BackingStoreException and logs a warning message if Windows
765      * registry is not available.
766      */

767     public void flush() throws BackingStoreException JavaDoc{
768        
769     if (isRemoved()) {
770         parent.flush();
771         return;
772     }
773         if (!isBackingStoreAvailable) {
774             throw new BackingStoreException JavaDoc(
775                                        "flush(): Backing store not available.");
776         }
777         int nativeHandle = openKey(KEY_READ);
778         if (nativeHandle == NULL_NATIVE_HANDLE) {
779             throw new BackingStoreException JavaDoc("Could not open windows"
780             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
781             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");
782         }
783         int result = WindowsRegFlushKey1(nativeHandle);
784         if (result != ERROR_SUCCESS) {
785             String JavaDoc info = "Could not flush windows "
786             + "registry node " + byteArrayToString(windowsAbsolutePath())
787             + " at root 0x" + Integer.toHexString(rootNativeHandle()) +
788             ". Windows RegFlushKey(...) returned error code " + result + ".";
789             logger().warning(info);
790             throw new BackingStoreException JavaDoc(info);
791         }
792         closeKey(nativeHandle);
793     }
794     
795
796     /**
797      * Implements <tt>Preferences</tt> <tt>sync()</tt> method.
798      * Flushes Windows registry changes to disk. Equivalent to flush().
799      * @see flush()
800      */

801     public void sync() throws BackingStoreException JavaDoc{
802         if (isRemoved())
803             throw new IllegalStateException JavaDoc("Node has been removed");
804         flush();
805     }
806
807     /**
808      * Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method.
809      * Constructs a child node with a
810      * given name and creates its underlying Windows registry node,
811      * if it does not exist.
812      * Logs a warning message, if Windows Registry is unavailable.
813      */

814     protected AbstractPreferences JavaDoc childSpi(String JavaDoc name) {
815             return new WindowsPreferences JavaDoc(this, name);
816     }
817
818     /**
819      * Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method.
820      * Deletes underlying Windows registry node.
821      * Throws a BackingStoreException and logs a warning, if Windows registry
822      * is not available.
823      */

824     public void removeNodeSpi() throws BackingStoreException JavaDoc {
825         int parentNativeHandle =
826                          ((WindowsPreferences JavaDoc)parent()).openKey(DELETE);
827         if (parentNativeHandle == NULL_NATIVE_HANDLE) {
828             throw new BackingStoreException JavaDoc("Could not open parent windows"
829             + "registry node of " + byteArrayToString(windowsAbsolutePath()) +
830             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");
831         }
832         int result =
833                 WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name()));
834         if (result != ERROR_SUCCESS) {
835             String JavaDoc info = "Could not delete windows "
836             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
837             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
838             ". Windows RegDeleteKeyEx(...) returned error code " +
839             result + ".";
840             logger().warning(info);
841             throw new BackingStoreException JavaDoc(info);
842         }
843         closeKey(parentNativeHandle);
844     }
845
846     /**
847      * Converts value's or node's name from its byte array representation to
848      * java string. Two encodings, simple and altBase64 are used. See
849      * {@link #toWindowsName(String) toWindowsName()} for a detailed
850      * description of encoding conventions.
851      * @param windowsNameArray Null-terminated byte array.
852      */

853     private static String JavaDoc toJavaName(byte[] windowsNameArray) {
854         String JavaDoc windowsName = byteArrayToString(windowsNameArray);
855         // check if Alt64
856
if ((windowsName.length()>1) &&
857                                    (windowsName.substring(0,2).equals("/!"))) {
858             return toJavaAlt64Name(windowsName);
859         }
860         StringBuffer JavaDoc javaName = new StringBuffer JavaDoc();
861         char ch;
862         // Decode from simple encoding
863
for (int i = 0; i < windowsName.length(); i++){
864             if ((ch = windowsName.charAt(i)) == '/') {
865                 char next = ' ';
866                 if ((windowsName.length() > i + 1) &&
867                    ((next = windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) {
868                 ch = next;
869                 i++;
870                 } else if ((windowsName.length() > i + 1) && (next == '/')) {
871                 ch = '\\';
872                 i++;
873                 }
874             } else if (ch == '\\') {
875                 ch = '/';
876             }
877             javaName.append(ch);
878         }
879         return javaName.toString();
880     }
881     
882     /**
883      * Converts value's or node's name from its Windows representation to java
884      * string, using altBase64 encoding. See
885      * {@link #toWindowsName(String) toWindowsName()} for a detailed
886      * description of encoding conventions.
887      */

888     
889     private static String JavaDoc toJavaAlt64Name(String JavaDoc windowsName) {
890         byte[] byteBuffer =
891                           Base64.altBase64ToByteArray(windowsName.substring(2));
892         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
893         for (int i = 0; i < byteBuffer.length; i++) {
894             int firstbyte = (byteBuffer[i++] & 0xff);
895             int secondbyte = (byteBuffer[i] & 0xff);
896             result.append((char)((firstbyte << 8) + secondbyte));
897         }
898         return result.toString();
899     }
900
901     /**
902      * Converts value's or node's name to its Windows representation
903      * as a byte-encoded string.
904      * Two encodings, simple and altBase64 are used.
905      * <p>
906      * <i>Simple</i> encoding is used, if java string does not contain
907      * any characters less, than 0x0020, or greater, than 0x007f.
908      * Simple encoding adds "/" character to capital letters, i.e.
909      * "A" is encoded as "/A". Character '\' is encoded as '//',
910      * '/' is encoded as '\'.
911      * The constructed string is converted to byte array by truncating the
912      * highest byte and adding the terminating <tt>null</tt> character.
913      * <p>
914      * <i>altBase64</i> encoding is used, if java string does contain at least
915      * one character less, than 0x0020, or greater, than 0x007f.
916      * This encoding is marked by setting first two bytes of the
917      * Windows string to '/!'. The java name is then encoded using
918      * byteArrayToAltBase64() method from
919      * Base64 class.
920      */

921     private static byte[] toWindowsName(String JavaDoc javaName) {
922         StringBuffer JavaDoc windowsName = new StringBuffer JavaDoc();
923         for (int i = 0; i < javaName.length(); i++) {
924             char ch =javaName.charAt(i);
925             if ((ch < 0x0020)||(ch > 0x007f)) {
926                 // If a non-trivial character encountered, use altBase64
927
return toWindowsAlt64Name(javaName);
928             }
929             if (ch == '\\') {
930                 windowsName.append("//");
931             } else if (ch == '/') {
932                 windowsName.append('\\');
933             } else if ((ch >= 'A') && (ch <='Z')) {
934                 windowsName.append("/" + ch);
935             } else {
936                 windowsName.append(ch);
937             }
938         }
939         return stringToByteArray(windowsName.toString());
940     }
941
942     /**
943      * Converts value's or node's name to its Windows representation
944      * as a byte-encoded string, using altBase64 encoding. See
945      * {@link #toWindowsName(String) toWindowsName()} for a detailed
946      * description of encoding conventions.
947      */

948     private static byte[] toWindowsAlt64Name(String JavaDoc javaName) {
949         byte[] javaNameArray = new byte[2*javaName.length()];
950         // Convert to byte pairs
951
int counter = 0;
952         for (int i = 0; i < javaName.length();i++) {
953                 int ch = javaName.charAt(i);
954                 javaNameArray[counter++] = (byte)(ch >>> 8);
955             javaNameArray[counter++] = (byte)ch;
956         }
957     
958         return stringToByteArray(
959                            "/!" + Base64.byteArrayToAltBase64(javaNameArray));
960     }
961
962     /**
963      * Converts value string from its Windows representation
964      * to java string. See
965      * {@link #toWindowsValueString(String) toWindowsValueString()} for the
966      * description of the encoding algorithm.
967      */

968      private static String JavaDoc toJavaValueString(byte[] windowsNameArray) {
969         // Use modified native2ascii algorithm
970
String JavaDoc windowsName = byteArrayToString(windowsNameArray);
971         StringBuffer JavaDoc javaName = new StringBuffer JavaDoc();
972         char ch;
973         for (int i = 0; i < windowsName.length(); i++){
974             if ((ch = windowsName.charAt(i)) == '/') {
975                 char next = ' ';
976                 
977                 if (windowsName.length() > i + 1 &&
978                                     (next = windowsName.charAt(i + 1)) == 'u') {
979                     if (windowsName.length() < i + 6){
980                         break;
981                     } else {
982                         ch = (char)Integer.parseInt
983                                       (windowsName.substring(i + 2, i + 6), 16);
984                         i += 5;
985                     }
986                 } else
987                 if ((windowsName.length() > i + 1) &&
988                           ((windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) {
989                 ch = next;
990                 i++;
991                 } else if ((windowsName.length() > i + 1) &&
992                                                (next == '/')) {
993                 ch = '\\';
994                 i++;
995                 }
996             } else if (ch == '\\') {
997                 ch = '/';
998             }
999             javaName.append(ch);
1000        }
1001        return javaName.toString();
1002    }
1003
1004    /**
1005     * Converts value string to it Windows representation.
1006     * as a byte-encoded string.
1007     * Encoding algorithm adds "/" character to capital letters, i.e.
1008     * "A" is encoded as "/A". Character '\' is encoded as '//',
1009     * '/' is encoded as '\'.
1010     * Then encoding scheme similar to jdk's native2ascii converter is used
1011     * to convert java string to a byte array of ASCII characters.
1012     */

1013    private static byte[] toWindowsValueString(String JavaDoc javaName) {
1014        StringBuffer JavaDoc windowsName = new StringBuffer JavaDoc();
1015        for (int i = 0; i < javaName.length(); i++) {
1016            char ch =javaName.charAt(i);
1017            if ((ch < 0x0020)||(ch > 0x007f)){
1018                // write \udddd
1019
windowsName.append("/u");
1020                String JavaDoc hex = Integer.toHexString(javaName.charAt(i));
1021                StringBuffer JavaDoc hex4 = new StringBuffer JavaDoc(hex);
1022                hex4.reverse();
1023                int len = 4 - hex4.length();
1024                for (int j = 0; j < len; j++){
1025                    hex4.append('0');
1026                }
1027                for (int j = 0; j < 4; j++){
1028                    windowsName.append(hex4.charAt(3 - j));
1029                }
1030            } else if (ch == '\\') {
1031                windowsName.append("//");
1032            } else if (ch == '/') {
1033                windowsName.append('\\');
1034            } else if ((ch >= 'A') && (ch <='Z')) {
1035                windowsName.append("/" + ch);
1036            } else {
1037                windowsName.append(ch);
1038            }
1039        }
1040        return stringToByteArray(windowsName.toString());
1041    }
1042    
1043    /**
1044     * Returns native handle for the top Windows node for this node.
1045     */

1046    private int rootNativeHandle() {
1047        return (isUserNode()? USER_ROOT_NATIVE_HANDLE :
1048                              SYSTEM_ROOT_NATIVE_HANDLE);
1049    }
1050    
1051    /**
1052     * Returns this java string as a null-terminated byte array
1053     */

1054    private static byte[] stringToByteArray(String JavaDoc str) {
1055        byte[] result = new byte[str.length()+1];
1056        for (int i = 0; i < str.length(); i++) {
1057            result[i] = (byte) str.charAt(i);
1058        }
1059        result[str.length()] = 0;
1060        return result;
1061    }
1062    
1063    /**
1064     * Converts a null-terminated byte array to java string
1065     */

1066    private static String JavaDoc byteArrayToString(byte[] array) {
1067        StringBuffer JavaDoc result = new StringBuffer JavaDoc();
1068        for (int i = 0; i < array.length - 1; i++) {
1069            result.append((char)array[i]);
1070        }
1071        return result.toString();
1072    }
1073    
1074   /**
1075    * Empty, never used implementation of AbstractPreferences.flushSpi().
1076    */

1077    protected void flushSpi() throws BackingStoreException JavaDoc {
1078        // assert false;
1079
}
1080    
1081   /**
1082    * Empty, never used implementation of AbstractPreferences.flushSpi().
1083    */

1084    protected void syncSpi() throws BackingStoreException JavaDoc {
1085        // assert false;
1086
}
1087
1088    private static synchronized Logger JavaDoc logger() {
1089        if (logger == null) {
1090            logger = Logger.getLogger("java.util.prefs");
1091        }
1092        return logger;
1093    }
1094}
1095
Popular Tags