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