Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/java/util/prefs/WindowsPreferences.java
32288 views
/*1* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util.prefs;2627import java.util.Map;28import java.util.TreeMap;29import java.util.StringTokenizer;30import java.io.ByteArrayOutputStream;31import sun.util.logging.PlatformLogger;3233/**34* Windows registry based implementation of <tt>Preferences</tt>.35* <tt>Preferences</tt>' <tt>systemRoot</tt> and <tt>userRoot</tt> are stored in36* <tt>HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs</tt> and37* <tt>HKEY_CURRENT_USER\Software\JavaSoft\Prefs</tt> correspondingly.38*39* @author Konstantin Kladko40* @see Preferences41* @see PreferencesFactory42* @since 1.443*/4445class WindowsPreferences extends AbstractPreferences{4647/**48* Logger for error messages49*/50private static PlatformLogger logger;5152/**53* Windows registry path to <tt>Preferences</tt>'s root nodes.54*/55private static final byte[] WINDOWS_ROOT_PATH =56stringToByteArray("Software\\JavaSoft\\Prefs");5758/**59* Windows handles to <tt>HKEY_CURRENT_USER</tt> and60* <tt>HKEY_LOCAL_MACHINE</tt> hives.61*/62private static final int HKEY_CURRENT_USER = 0x80000001;63private static final int HKEY_LOCAL_MACHINE = 0x80000002;6465/**66* Mount point for <tt>Preferences</tt>' user root.67*/68private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER;6970/**71* Mount point for <tt>Preferences</tt>' system root.72*/73private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE;7475/**76* Maximum byte-encoded path length for Windows native functions,77* ending <tt>null</tt> character not included.78*/79private static final int MAX_WINDOWS_PATH_LENGTH = 256;8081/**82* User root node.83*/84private static volatile Preferences userRoot;8586static Preferences getUserRoot() {87Preferences root = userRoot;88if (root == null) {89synchronized (WindowsPreferences.class) {90root = userRoot;91if (root == null) {92root = new WindowsPreferences(USER_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);93userRoot = root;94}95}96}97return root;98}99100/**101* System root node.102*/103private static volatile Preferences systemRoot;104105static Preferences getSystemRoot() {106Preferences root = systemRoot;107if (root == null) {108synchronized (WindowsPreferences.class) {109root = systemRoot;110if (root == null) {111root = new WindowsPreferences(SYSTEM_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);112systemRoot = root;113}114}115}116return root;117}118119/* Windows error codes. */120private static final int ERROR_SUCCESS = 0;121private static final int ERROR_FILE_NOT_FOUND = 2;122private static final int ERROR_ACCESS_DENIED = 5;123124/* Constants used to interpret returns of native functions */125private static final int NATIVE_HANDLE = 0;126private static final int ERROR_CODE = 1;127private static final int SUBKEYS_NUMBER = 0;128private static final int VALUES_NUMBER = 2;129private static final int MAX_KEY_LENGTH = 3;130private static final int MAX_VALUE_NAME_LENGTH = 4;131private static final int DISPOSITION = 2;132private static final int REG_CREATED_NEW_KEY = 1;133private static final int REG_OPENED_EXISTING_KEY = 2;134private static final int NULL_NATIVE_HANDLE = 0;135136/* Windows security masks */137private static final int DELETE = 0x10000;138private static final int KEY_QUERY_VALUE = 1;139private static final int KEY_SET_VALUE = 2;140private static final int KEY_CREATE_SUB_KEY = 4;141private static final int KEY_ENUMERATE_SUB_KEYS = 8;142private static final int KEY_READ = 0x20019;143private static final int KEY_WRITE = 0x20006;144private static final int KEY_ALL_ACCESS = 0xf003f;145146/**147* Initial time between registry access attempts, in ms. The time is doubled148* after each failing attempt (except the first).149*/150private static int INIT_SLEEP_TIME = 50;151152/**153* Maximum number of registry access attempts.154*/155private static int MAX_ATTEMPTS = 5;156157/**158* BackingStore availability flag.159*/160private boolean isBackingStoreAvailable = true;161162/**163* Java wrapper for Windows registry API RegOpenKey()164*/165private static native int[] WindowsRegOpenKey(int hKey, byte[] subKey,166int securityMask);167/**168* Retries RegOpenKey() MAX_ATTEMPTS times before giving up.169*/170private static int[] WindowsRegOpenKey1(int hKey, byte[] subKey,171int securityMask) {172int[] result = WindowsRegOpenKey(hKey, subKey, securityMask);173if (result[ERROR_CODE] == ERROR_SUCCESS) {174return result;175} else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) {176logger().warning("Trying to recreate Windows registry node " +177byteArrayToString(subKey) + " at root 0x" +178Integer.toHexString(hKey) + ".");179// Try recreation180int handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE];181WindowsRegCloseKey(handle);182return WindowsRegOpenKey(hKey, subKey, securityMask);183} else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) {184long sleepTime = INIT_SLEEP_TIME;185for (int i = 0; i < MAX_ATTEMPTS; i++) {186try {187Thread.sleep(sleepTime);188} catch(InterruptedException e) {189return result;190}191sleepTime *= 2;192result = WindowsRegOpenKey(hKey, subKey, securityMask);193if (result[ERROR_CODE] == ERROR_SUCCESS) {194return result;195}196}197}198return result;199}200201/**202* Java wrapper for Windows registry API RegCloseKey()203*/204private static native int WindowsRegCloseKey(int hKey);205206/**207* Java wrapper for Windows registry API RegCreateKeyEx()208*/209private static native int[] WindowsRegCreateKeyEx(int hKey, byte[] subKey);210211/**212* Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.213*/214private static int[] WindowsRegCreateKeyEx1(int hKey, byte[] subKey) {215int[] result = WindowsRegCreateKeyEx(hKey, subKey);216if (result[ERROR_CODE] == ERROR_SUCCESS) {217return result;218} else {219long sleepTime = INIT_SLEEP_TIME;220for (int i = 0; i < MAX_ATTEMPTS; i++) {221try {222Thread.sleep(sleepTime);223} catch(InterruptedException e) {224return result;225}226sleepTime *= 2;227result = WindowsRegCreateKeyEx(hKey, subKey);228if (result[ERROR_CODE] == ERROR_SUCCESS) {229return result;230}231}232}233return result;234}235/**236* Java wrapper for Windows registry API RegDeleteKey()237*/238private static native int WindowsRegDeleteKey(int hKey, byte[] subKey);239240/**241* Java wrapper for Windows registry API RegFlushKey()242*/243private static native int WindowsRegFlushKey(int hKey);244245/**246* Retries RegFlushKey() MAX_ATTEMPTS times before giving up.247*/248private static int WindowsRegFlushKey1(int hKey) {249int result = WindowsRegFlushKey(hKey);250if (result == ERROR_SUCCESS) {251return result;252} else {253long sleepTime = INIT_SLEEP_TIME;254for (int i = 0; i < MAX_ATTEMPTS; i++) {255try {256Thread.sleep(sleepTime);257} catch(InterruptedException e) {258return result;259}260sleepTime *= 2;261result = WindowsRegFlushKey(hKey);262if (result == ERROR_SUCCESS) {263return result;264}265}266}267return result;268}269270/**271* Java wrapper for Windows registry API RegQueryValueEx()272*/273private static native byte[] WindowsRegQueryValueEx(int hKey,274byte[] valueName);275/**276* Java wrapper for Windows registry API RegSetValueEx()277*/278private static native int WindowsRegSetValueEx(int hKey, byte[] valueName,279byte[] value);280/**281* Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.282*/283private static int WindowsRegSetValueEx1(int hKey, byte[] valueName,284byte[] value) {285int result = WindowsRegSetValueEx(hKey, valueName, value);286if (result == ERROR_SUCCESS) {287return result;288} else {289long sleepTime = INIT_SLEEP_TIME;290for (int i = 0; i < MAX_ATTEMPTS; i++) {291try {292Thread.sleep(sleepTime);293} catch(InterruptedException e) {294return result;295}296sleepTime *= 2;297result = WindowsRegSetValueEx(hKey, valueName, value);298if (result == ERROR_SUCCESS) {299return result;300}301}302}303return result;304}305306/**307* Java wrapper for Windows registry API RegDeleteValue()308*/309private static native int WindowsRegDeleteValue(int hKey, byte[] valueName);310311/**312* Java wrapper for Windows registry API RegQueryInfoKey()313*/314private static native int[] WindowsRegQueryInfoKey(int hKey);315316/**317* Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.318*/319private static int[] WindowsRegQueryInfoKey1(int hKey) {320int[] result = WindowsRegQueryInfoKey(hKey);321if (result[ERROR_CODE] == ERROR_SUCCESS) {322return result;323} else {324long sleepTime = INIT_SLEEP_TIME;325for (int i = 0; i < MAX_ATTEMPTS; i++) {326try {327Thread.sleep(sleepTime);328} catch(InterruptedException e) {329return result;330}331sleepTime *= 2;332result = WindowsRegQueryInfoKey(hKey);333if (result[ERROR_CODE] == ERROR_SUCCESS) {334return result;335}336}337}338return result;339}340341/**342* Java wrapper for Windows registry API RegEnumKeyEx()343*/344private static native byte[] WindowsRegEnumKeyEx(int hKey, int subKeyIndex,345int maxKeyLength);346347/**348* Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up.349*/350private static byte[] WindowsRegEnumKeyEx1(int hKey, int subKeyIndex,351int maxKeyLength) {352byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);353if (result != null) {354return result;355} else {356long sleepTime = INIT_SLEEP_TIME;357for (int i = 0; i < MAX_ATTEMPTS; i++) {358try {359Thread.sleep(sleepTime);360} catch(InterruptedException e) {361return result;362}363sleepTime *= 2;364result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);365if (result != null) {366return result;367}368}369}370return result;371}372373/**374* Java wrapper for Windows registry API RegEnumValue()375*/376private static native byte[] WindowsRegEnumValue(int hKey, int valueIndex,377int maxValueNameLength);378/**379* Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.380*/381private static byte[] WindowsRegEnumValue1(int hKey, int valueIndex,382int maxValueNameLength) {383byte[] result = WindowsRegEnumValue(hKey, valueIndex,384maxValueNameLength);385if (result != null) {386return result;387} else {388long sleepTime = INIT_SLEEP_TIME;389for (int i = 0; i < MAX_ATTEMPTS; i++) {390try {391Thread.sleep(sleepTime);392} catch(InterruptedException e) {393return result;394}395sleepTime *= 2;396result = WindowsRegEnumValue(hKey, valueIndex,397maxValueNameLength);398if (result != null) {399return result;400}401}402}403return result;404}405406/**407* Constructs a <tt>WindowsPreferences</tt> node, creating underlying408* Windows registry node and all its Windows parents, if they are not yet409* created.410* Logs a warning message, if Windows Registry is unavailable.411*/412private WindowsPreferences(WindowsPreferences parent, String name) {413super(parent, name);414int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ);415if (parentNativeHandle == NULL_NATIVE_HANDLE) {416// if here, openKey failed and logged417isBackingStoreAvailable = false;418return;419}420int[] result =421WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name));422if (result[ERROR_CODE] != ERROR_SUCCESS) {423logger().warning("Could not create windows registry node " +424byteArrayToString(windowsAbsolutePath()) +425" at root 0x" + Integer.toHexString(rootNativeHandle()) +426". Windows RegCreateKeyEx(...) returned error code " +427result[ERROR_CODE] + ".");428isBackingStoreAvailable = false;429return;430}431newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);432closeKey(parentNativeHandle);433closeKey(result[NATIVE_HANDLE]);434}435436/**437* Constructs a root node creating the underlying438* Windows registry node and all of its parents, if they have not yet been439* created.440* Logs a warning message, if Windows Registry is unavailable.441* @param rootNativeHandle Native handle to one of Windows top level keys.442* @param rootDirectory Path to root directory, as a byte-encoded string.443*/444private WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) {445super(null, "");446int[] result =447WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory);448if (result[ERROR_CODE] != ERROR_SUCCESS) {449logger().warning("Could not open/create prefs root node " +450byteArrayToString(windowsAbsolutePath()) +451" at root 0x" + Integer.toHexString(rootNativeHandle()) +452". Windows RegCreateKeyEx(...) returned error code " +453result[ERROR_CODE] + ".");454isBackingStoreAvailable = false;455return;456}457// Check if a new node458newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);459closeKey(result[NATIVE_HANDLE]);460}461462/**463* Returns Windows absolute path of the current node as a byte array.464* Java "/" separator is transformed into Windows "\".465* @see Preferences#absolutePath()466*/467private byte[] windowsAbsolutePath() {468ByteArrayOutputStream bstream = new ByteArrayOutputStream();469bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);470StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/");471while (tokenizer.hasMoreTokens()) {472bstream.write((byte)'\\');473String nextName = tokenizer.nextToken();474byte[] windowsNextName = toWindowsName(nextName);475bstream.write(windowsNextName, 0, windowsNextName.length-1);476}477bstream.write(0);478return bstream.toByteArray();479}480481/**482* Opens current node's underlying Windows registry key using a483* given security mask.484* @param securityMask Windows security mask.485* @return Windows registry key's handle.486* @see #openKey(byte[], int)487* @see #openKey(int, byte[], int)488* @see #closeKey(int)489*/490private int openKey(int securityMask) {491return openKey(securityMask, securityMask);492}493494/**495* Opens current node's underlying Windows registry key using a496* given security mask.497* @param mask1 Preferred Windows security mask.498* @param mask2 Alternate Windows security mask.499* @return Windows registry key's handle.500* @see #openKey(byte[], int)501* @see #openKey(int, byte[], int)502* @see #closeKey(int)503*/504private int openKey(int mask1, int mask2) {505return openKey(windowsAbsolutePath(), mask1, mask2);506}507508/**509* Opens Windows registry key at a given absolute path using a given510* security mask.511* @param windowsAbsolutePath Windows absolute path of the512* key as a byte-encoded string.513* @param mask1 Preferred Windows security mask.514* @param mask2 Alternate Windows security mask.515* @return Windows registry key's handle.516* @see #openKey(int)517* @see #openKey(int, byte[],int)518* @see #closeKey(int)519*/520private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {521/* Check if key's path is short enough be opened at once522otherwise use a path-splitting procedure */523if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {524int[] result = WindowsRegOpenKey1(rootNativeHandle(),525windowsAbsolutePath, mask1);526if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)527result = WindowsRegOpenKey1(rootNativeHandle(),528windowsAbsolutePath, mask2);529530if (result[ERROR_CODE] != ERROR_SUCCESS) {531logger().warning("Could not open windows registry node " +532byteArrayToString(windowsAbsolutePath()) +533" at root 0x" +534Integer.toHexString(rootNativeHandle()) +535". Windows RegOpenKey(...) returned error code " +536result[ERROR_CODE] + ".");537result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;538if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {539throw new SecurityException(540"Could not open windows registry node " +541byteArrayToString(windowsAbsolutePath()) +542" at root 0x" +543Integer.toHexString(rootNativeHandle()) +544": Access denied");545}546}547return result[NATIVE_HANDLE];548} else {549return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);550}551}552553/**554* Opens Windows registry key at a given relative path555* with respect to a given Windows registry key.556* @param windowsAbsolutePath Windows relative path of the557* key as a byte-encoded string.558* @param nativeHandle handle to the base Windows key.559* @param mask1 Preferred Windows security mask.560* @param mask2 Alternate Windows security mask.561* @return Windows registry key's handle.562* @see #openKey(int)563* @see #openKey(byte[],int)564* @see #closeKey(int)565*/566private int openKey(int nativeHandle, byte[] windowsRelativePath,567int mask1, int mask2) {568/* If the path is short enough open at once. Otherwise split the path */569if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) {570int[] result = WindowsRegOpenKey1(nativeHandle,571windowsRelativePath, mask1);572if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)573result = WindowsRegOpenKey1(nativeHandle,574windowsRelativePath, mask2);575576if (result[ERROR_CODE] != ERROR_SUCCESS) {577logger().warning("Could not open windows registry node " +578byteArrayToString(windowsAbsolutePath()) +579" at root 0x" + Integer.toHexString(nativeHandle) +580". Windows RegOpenKey(...) returned error code " +581result[ERROR_CODE] + ".");582result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;583}584return result[NATIVE_HANDLE];585} else {586int separatorPosition = -1;587// Be greedy - open the longest possible path588for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {589if (windowsRelativePath[i] == ((byte)'\\')) {590separatorPosition = i;591break;592}593}594// Split the path and do the recursion595byte[] nextRelativeRoot = new byte[separatorPosition+1];596System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0,597separatorPosition);598nextRelativeRoot[separatorPosition] = 0;599byte[] nextRelativePath = new byte[windowsRelativePath.length -600separatorPosition - 1];601System.arraycopy(windowsRelativePath, separatorPosition+1,602nextRelativePath, 0, nextRelativePath.length);603int nextNativeHandle = openKey(nativeHandle, nextRelativeRoot,604mask1, mask2);605if (nextNativeHandle == NULL_NATIVE_HANDLE) {606return NULL_NATIVE_HANDLE;607}608int result = openKey(nextNativeHandle, nextRelativePath,609mask1,mask2);610closeKey(nextNativeHandle);611return result;612}613}614615/**616* Closes Windows registry key.617* Logs a warning if Windows registry is unavailable.618* @param key's Windows registry handle.619* @see #openKey(int)620* @see #openKey(byte[],int)621* @see #openKey(int, byte[],int)622*/623private void closeKey(int nativeHandle) {624int result = WindowsRegCloseKey(nativeHandle);625if (result != ERROR_SUCCESS) {626logger().warning("Could not close windows registry node " +627byteArrayToString(windowsAbsolutePath()) +628" at root 0x" +629Integer.toHexString(rootNativeHandle()) +630". Windows RegCloseKey(...) returned error code " +631result + ".");632}633}634635/**636* Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method.637* Puts name-value pair into the underlying Windows registry node.638* Logs a warning, if Windows registry is unavailable.639* @see #getSpi(String)640*/641protected void putSpi(String javaName, String value) {642int nativeHandle = openKey(KEY_SET_VALUE);643if (nativeHandle == NULL_NATIVE_HANDLE) {644isBackingStoreAvailable = false;645return;646}647int result = WindowsRegSetValueEx1(nativeHandle,648toWindowsName(javaName), toWindowsValueString(value));649if (result != ERROR_SUCCESS) {650logger().warning("Could not assign value to key " +651byteArrayToString(toWindowsName(javaName)) +652" at Windows registry node " +653byteArrayToString(windowsAbsolutePath()) +654" at root 0x" +655Integer.toHexString(rootNativeHandle()) +656". Windows RegSetValueEx(...) returned error code " +657result + ".");658isBackingStoreAvailable = false;659}660closeKey(nativeHandle);661}662663/**664* Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method.665* Gets a string value from the underlying Windows registry node.666* Logs a warning, if Windows registry is unavailable.667* @see #putSpi(String, String)668*/669protected String getSpi(String javaName) {670int nativeHandle = openKey(KEY_QUERY_VALUE);671if (nativeHandle == NULL_NATIVE_HANDLE) {672return null;673}674Object resultObject = WindowsRegQueryValueEx(nativeHandle,675toWindowsName(javaName));676if (resultObject == null) {677closeKey(nativeHandle);678return null;679}680closeKey(nativeHandle);681return toJavaValueString((byte[]) resultObject);682}683684/**685* Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method.686* Deletes a string name-value pair from the underlying Windows registry687* node, if this value still exists.688* Logs a warning, if Windows registry is unavailable or key has already689* been deleted.690*/691protected void removeSpi(String key) {692int nativeHandle = openKey(KEY_SET_VALUE);693if (nativeHandle == NULL_NATIVE_HANDLE) {694return;695}696int result =697WindowsRegDeleteValue(nativeHandle, toWindowsName(key));698if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {699logger().warning("Could not delete windows registry value " +700byteArrayToString(windowsAbsolutePath()) + "\\" +701toWindowsName(key) + " at root 0x" +702Integer.toHexString(rootNativeHandle()) +703". Windows RegDeleteValue(...) returned error code " +704result + ".");705isBackingStoreAvailable = false;706}707closeKey(nativeHandle);708}709710/**711* Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method.712* Gets value names from the underlying Windows registry node.713* Throws a BackingStoreException and logs a warning, if714* Windows registry is unavailable.715*/716protected String[] keysSpi() throws BackingStoreException{717// Find out the number of values718int nativeHandle = openKey(KEY_QUERY_VALUE);719if (nativeHandle == NULL_NATIVE_HANDLE) {720throw new BackingStoreException(721"Could not open windows registry node " +722byteArrayToString(windowsAbsolutePath()) +723" at root 0x" +724Integer.toHexString(rootNativeHandle()) + ".");725}726int[] result = WindowsRegQueryInfoKey1(nativeHandle);727if (result[ERROR_CODE] != ERROR_SUCCESS) {728String info = "Could not query windows registry node " +729byteArrayToString(windowsAbsolutePath()) +730" at root 0x" +731Integer.toHexString(rootNativeHandle()) +732". Windows RegQueryInfoKeyEx(...) returned error code " +733result[ERROR_CODE] + ".";734logger().warning(info);735throw new BackingStoreException(info);736}737int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH];738int valuesNumber = result[VALUES_NUMBER];739if (valuesNumber == 0) {740closeKey(nativeHandle);741return new String[0];742}743// Get the values744String[] valueNames = new String[valuesNumber];745for (int i = 0; i < valuesNumber; i++) {746byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,747maxValueNameLength+1);748if (windowsName == null) {749String info =750"Could not enumerate value #" + i + " of windows node " +751byteArrayToString(windowsAbsolutePath()) + " at root 0x" +752Integer.toHexString(rootNativeHandle()) + ".";753logger().warning(info);754throw new BackingStoreException(info);755}756valueNames[i] = toJavaName(windowsName);757}758closeKey(nativeHandle);759return valueNames;760}761762/**763* Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method.764* Calls Windows registry to retrive children of this node.765* Throws a BackingStoreException and logs a warning message,766* if Windows registry is not available.767*/768protected String[] childrenNamesSpi() throws BackingStoreException {769// Open key770int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE);771if (nativeHandle == NULL_NATIVE_HANDLE) {772throw new BackingStoreException(773"Could not open windows registry node " +774byteArrayToString(windowsAbsolutePath()) +775" at root 0x" +776Integer.toHexString(rootNativeHandle()) + ".");777}778// Get number of children779int[] result = WindowsRegQueryInfoKey1(nativeHandle);780if (result[ERROR_CODE] != ERROR_SUCCESS) {781String info = "Could not query windows registry node " +782byteArrayToString(windowsAbsolutePath()) +783" at root 0x" + Integer.toHexString(rootNativeHandle()) +784". Windows RegQueryInfoKeyEx(...) returned error code " +785result[ERROR_CODE] + ".";786logger().warning(info);787throw new BackingStoreException(info);788}789int maxKeyLength = result[MAX_KEY_LENGTH];790int subKeysNumber = result[SUBKEYS_NUMBER];791if (subKeysNumber == 0) {792closeKey(nativeHandle);793return new String[0];794}795String[] subkeys = new String[subKeysNumber];796String[] children = new String[subKeysNumber];797// Get children798for (int i = 0; i < subKeysNumber; i++) {799byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,800maxKeyLength+1);801if (windowsName == null) {802String info =803"Could not enumerate key #" + i + " of windows node " +804byteArrayToString(windowsAbsolutePath()) + " at root 0x" +805Integer.toHexString(rootNativeHandle()) + ". ";806logger().warning(info);807throw new BackingStoreException(info);808}809String javaName = toJavaName(windowsName);810children[i] = javaName;811}812closeKey(nativeHandle);813return children;814}815816/**817* Implements <tt>Preferences</tt> <tt>flush()</tt> method.818* Flushes Windows registry changes to disk.819* Throws a BackingStoreException and logs a warning message if Windows820* registry is not available.821*/822public void flush() throws BackingStoreException{823824if (isRemoved()) {825parent.flush();826return;827}828if (!isBackingStoreAvailable) {829throw new BackingStoreException(830"flush(): Backing store not available.");831}832int nativeHandle = openKey(KEY_READ);833if (nativeHandle == NULL_NATIVE_HANDLE) {834throw new BackingStoreException(835"Could not open windows registry node " +836byteArrayToString(windowsAbsolutePath()) +837" at root 0x" +838Integer.toHexString(rootNativeHandle()) + ".");839}840int result = WindowsRegFlushKey1(nativeHandle);841if (result != ERROR_SUCCESS) {842String info = "Could not flush windows registry node " +843byteArrayToString(windowsAbsolutePath()) +844" at root 0x" +845Integer.toHexString(rootNativeHandle()) +846". Windows RegFlushKey(...) returned error code " +847result + ".";848logger().warning(info);849throw new BackingStoreException(info);850}851closeKey(nativeHandle);852}853854855/**856* Implements <tt>Preferences</tt> <tt>sync()</tt> method.857* Flushes Windows registry changes to disk. Equivalent to flush().858* @see flush()859*/860public void sync() throws BackingStoreException{861if (isRemoved())862throw new IllegalStateException("Node has been removed");863flush();864}865866/**867* Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method.868* Constructs a child node with a869* given name and creates its underlying Windows registry node,870* if it does not exist.871* Logs a warning message, if Windows Registry is unavailable.872*/873protected AbstractPreferences childSpi(String name) {874return new WindowsPreferences(this, name);875}876877/**878* Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method.879* Deletes underlying Windows registry node.880* Throws a BackingStoreException and logs a warning, if Windows registry881* is not available.882*/883public void removeNodeSpi() throws BackingStoreException {884int parentNativeHandle =885((WindowsPreferences)parent()).openKey(DELETE);886if (parentNativeHandle == NULL_NATIVE_HANDLE) {887throw new BackingStoreException(888"Could not open parent windows registry node of " +889byteArrayToString(windowsAbsolutePath()) +890" at root 0x" +891Integer.toHexString(rootNativeHandle()) + ".");892}893int result =894WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name()));895if (result != ERROR_SUCCESS) {896String info = "Could not delete windows registry node " +897byteArrayToString(windowsAbsolutePath()) +898" at root 0x" + Integer.toHexString(rootNativeHandle()) +899". Windows RegDeleteKeyEx(...) returned error code " +900result + ".";901logger().warning(info);902throw new BackingStoreException(info);903}904closeKey(parentNativeHandle);905}906907/**908* Converts value's or node's name from its byte array representation to909* java string. Two encodings, simple and altBase64 are used. See910* {@link #toWindowsName(String) toWindowsName()} for a detailed911* description of encoding conventions.912* @param windowsNameArray Null-terminated byte array.913*/914private static String toJavaName(byte[] windowsNameArray) {915String windowsName = byteArrayToString(windowsNameArray);916// check if Alt64917if ((windowsName.length() > 1) &&918(windowsName.substring(0, 2).equals("/!"))) {919return toJavaAlt64Name(windowsName);920}921StringBuilder javaName = new StringBuilder();922char ch;923// Decode from simple encoding924for (int i = 0; i < windowsName.length(); i++) {925if ((ch = windowsName.charAt(i)) == '/') {926char next = ' ';927if ((windowsName.length() > i + 1) &&928((next = windowsName.charAt(i+1)) >= 'A') &&929(next <= 'Z')) {930ch = next;931i++;932} else if ((windowsName.length() > i + 1) &&933(next == '/')) {934ch = '\\';935i++;936}937} else if (ch == '\\') {938ch = '/';939}940javaName.append(ch);941}942return javaName.toString();943}944945/**946* Converts value's or node's name from its Windows representation to java947* string, using altBase64 encoding. See948* {@link #toWindowsName(String) toWindowsName()} for a detailed949* description of encoding conventions.950*/951952private static String toJavaAlt64Name(String windowsName) {953byte[] byteBuffer =954Base64.altBase64ToByteArray(windowsName.substring(2));955StringBuilder result = new StringBuilder();956for (int i = 0; i < byteBuffer.length; i++) {957int firstbyte = (byteBuffer[i++] & 0xff);958int secondbyte = (byteBuffer[i] & 0xff);959result.append((char)((firstbyte << 8) + secondbyte));960}961return result.toString();962}963964/**965* Converts value's or node's name to its Windows representation966* as a byte-encoded string.967* Two encodings, simple and altBase64 are used.968* <p>969* <i>Simple</i> encoding is used, if java string does not contain970* any characters less, than 0x0020, or greater, than 0x007f.971* Simple encoding adds "/" character to capital letters, i.e.972* "A" is encoded as "/A". Character '\' is encoded as '//',973* '/' is encoded as '\'.974* The constructed string is converted to byte array by truncating the975* highest byte and adding the terminating <tt>null</tt> character.976* <p>977* <i>altBase64</i> encoding is used, if java string does contain at least978* one character less, than 0x0020, or greater, than 0x007f.979* This encoding is marked by setting first two bytes of the980* Windows string to '/!'. The java name is then encoded using981* byteArrayToAltBase64() method from982* Base64 class.983*/984private static byte[] toWindowsName(String javaName) {985StringBuilder windowsName = new StringBuilder();986for (int i = 0; i < javaName.length(); i++) {987char ch = javaName.charAt(i);988if ((ch < 0x0020) || (ch > 0x007f)) {989// If a non-trivial character encountered, use altBase64990return toWindowsAlt64Name(javaName);991}992if (ch == '\\') {993windowsName.append("//");994} else if (ch == '/') {995windowsName.append('\\');996} else if ((ch >= 'A') && (ch <='Z')) {997windowsName.append('/').append(ch);998} else {999windowsName.append(ch);1000}1001}1002return stringToByteArray(windowsName.toString());1003}10041005/**1006* Converts value's or node's name to its Windows representation1007* as a byte-encoded string, using altBase64 encoding. See1008* {@link #toWindowsName(String) toWindowsName()} for a detailed1009* description of encoding conventions.1010*/1011private static byte[] toWindowsAlt64Name(String javaName) {1012byte[] javaNameArray = new byte[2*javaName.length()];1013// Convert to byte pairs1014int counter = 0;1015for (int i = 0; i < javaName.length();i++) {1016int ch = javaName.charAt(i);1017javaNameArray[counter++] = (byte)(ch >>> 8);1018javaNameArray[counter++] = (byte)ch;1019}10201021return stringToByteArray("/!" +1022Base64.byteArrayToAltBase64(javaNameArray));1023}10241025/**1026* Converts value string from its Windows representation1027* to java string. See1028* {@link #toWindowsValueString(String) toWindowsValueString()} for the1029* description of the encoding algorithm.1030*/1031private static String toJavaValueString(byte[] windowsNameArray) {1032// Use modified native2ascii algorithm1033String windowsName = byteArrayToString(windowsNameArray);1034StringBuilder javaName = new StringBuilder();1035char ch;1036for (int i = 0; i < windowsName.length(); i++){1037if ((ch = windowsName.charAt(i)) == '/') {1038char next = ' ';10391040if (windowsName.length() > i + 1 &&1041(next = windowsName.charAt(i + 1)) == 'u') {1042if (windowsName.length() < i + 6) {1043break;1044} else {1045ch = (char)Integer.parseInt(1046windowsName.substring(i + 2, i + 6), 16);1047i += 5;1048}1049} else1050if ((windowsName.length() > i + 1) &&1051((windowsName.charAt(i+1)) >= 'A') &&1052(next <= 'Z')) {1053ch = next;1054i++;1055} else if ((windowsName.length() > i + 1) &&1056(next == '/')) {1057ch = '\\';1058i++;1059}1060} else if (ch == '\\') {1061ch = '/';1062}1063javaName.append(ch);1064}1065return javaName.toString();1066}10671068/**1069* Converts value string to it Windows representation.1070* as a byte-encoded string.1071* Encoding algorithm adds "/" character to capital letters, i.e.1072* "A" is encoded as "/A". Character '\' is encoded as '//',1073* '/' is encoded as '\'.1074* Then encoding scheme similar to jdk's native2ascii converter is used1075* to convert java string to a byte array of ASCII characters.1076*/1077private static byte[] toWindowsValueString(String javaName) {1078StringBuilder windowsName = new StringBuilder();1079for (int i = 0; i < javaName.length(); i++) {1080char ch = javaName.charAt(i);1081if ((ch < 0x0020) || (ch > 0x007f)){1082// write \udddd1083windowsName.append("/u");1084String hex = Integer.toHexString(javaName.charAt(i));1085StringBuilder hex4 = new StringBuilder(hex);1086hex4.reverse();1087int len = 4 - hex4.length();1088for (int j = 0; j < len; j++){1089hex4.append('0');1090}1091for (int j = 0; j < 4; j++){1092windowsName.append(hex4.charAt(3 - j));1093}1094} else if (ch == '\\') {1095windowsName.append("//");1096} else if (ch == '/') {1097windowsName.append('\\');1098} else if ((ch >= 'A') && (ch <='Z')) {1099windowsName.append('/').append(ch);1100} else {1101windowsName.append(ch);1102}1103}1104return stringToByteArray(windowsName.toString());1105}11061107/**1108* Returns native handle for the top Windows node for this node.1109*/1110private int rootNativeHandle() {1111return (isUserNode()1112? USER_ROOT_NATIVE_HANDLE1113: SYSTEM_ROOT_NATIVE_HANDLE);1114}11151116/**1117* Returns this java string as a null-terminated byte array1118*/1119private static byte[] stringToByteArray(String str) {1120byte[] result = new byte[str.length()+1];1121for (int i = 0; i < str.length(); i++) {1122result[i] = (byte) str.charAt(i);1123}1124result[str.length()] = 0;1125return result;1126}11271128/**1129* Converts a null-terminated byte array to java string1130*/1131private static String byteArrayToString(byte[] array) {1132StringBuilder result = new StringBuilder();1133for (int i = 0; i < array.length - 1; i++) {1134result.append((char)array[i]);1135}1136return result.toString();1137}11381139/**1140* Empty, never used implementation of AbstractPreferences.flushSpi().1141*/1142protected void flushSpi() throws BackingStoreException {1143// assert false;1144}11451146/**1147* Empty, never used implementation of AbstractPreferences.flushSpi().1148*/1149protected void syncSpi() throws BackingStoreException {1150// assert false;1151}11521153private static synchronized PlatformLogger logger() {1154if (logger == null) {1155logger = PlatformLogger.getLogger("java.util.prefs");1156}1157return logger;1158}1159}116011611162