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

Save This Page
Home » openjdk-7 » java » util » prefs » [javadoc | source]