You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1135 lines
43 KiB
1135 lines
43 KiB
/*
|
|
* Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package java.util.prefs;
|
|
|
|
import java.util.Map;
|
|
import java.util.TreeMap;
|
|
import java.util.StringTokenizer;
|
|
import java.io.ByteArrayOutputStream;
|
|
import sun.util.logging.PlatformLogger;
|
|
|
|
/**
|
|
* Windows registry based implementation of <tt>Preferences</tt>.
|
|
* <tt>Preferences</tt>' <tt>systemRoot</tt> and <tt>userRoot</tt> are stored in
|
|
* <tt>HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs</tt> and
|
|
* <tt>HKEY_CURRENT_USER\Software\JavaSoft\Prefs</tt> correspondingly.
|
|
*
|
|
* @author Konstantin Kladko
|
|
* @see Preferences
|
|
* @see PreferencesFactory
|
|
* @since 1.4
|
|
*/
|
|
|
|
class WindowsPreferences extends AbstractPreferences{
|
|
|
|
/**
|
|
* Logger for error messages
|
|
*/
|
|
private static PlatformLogger logger;
|
|
|
|
/**
|
|
* Windows registry path to <tt>Preferences</tt>'s root nodes.
|
|
*/
|
|
private static final byte[] WINDOWS_ROOT_PATH =
|
|
stringToByteArray("Software\\JavaSoft\\Prefs");
|
|
|
|
/**
|
|
* Windows handles to <tt>HKEY_CURRENT_USER</tt> and
|
|
* <tt>HKEY_LOCAL_MACHINE</tt> hives.
|
|
*/
|
|
private static final int HKEY_CURRENT_USER = 0x80000001;
|
|
private static final int HKEY_LOCAL_MACHINE = 0x80000002;
|
|
|
|
/**
|
|
* Mount point for <tt>Preferences</tt>' user root.
|
|
*/
|
|
private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER;
|
|
|
|
/**
|
|
* Mount point for <tt>Preferences</tt>' system root.
|
|
*/
|
|
private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE;
|
|
|
|
/**
|
|
* Maximum byte-encoded path length for Windows native functions,
|
|
* ending <tt>null</tt> character not included.
|
|
*/
|
|
private static final int MAX_WINDOWS_PATH_LENGTH = 256;
|
|
|
|
/**
|
|
* User root node.
|
|
*/
|
|
static final Preferences userRoot =
|
|
new WindowsPreferences(USER_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
|
|
|
|
/**
|
|
* System root node.
|
|
*/
|
|
static final Preferences systemRoot =
|
|
new WindowsPreferences(SYSTEM_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
|
|
|
|
/* Windows error codes. */
|
|
private static final int ERROR_SUCCESS = 0;
|
|
private static final int ERROR_FILE_NOT_FOUND = 2;
|
|
private static final int ERROR_ACCESS_DENIED = 5;
|
|
|
|
/* Constants used to interpret returns of native functions */
|
|
private static final int NATIVE_HANDLE = 0;
|
|
private static final int ERROR_CODE = 1;
|
|
private static final int SUBKEYS_NUMBER = 0;
|
|
private static final int VALUES_NUMBER = 2;
|
|
private static final int MAX_KEY_LENGTH = 3;
|
|
private static final int MAX_VALUE_NAME_LENGTH = 4;
|
|
private static final int DISPOSITION = 2;
|
|
private static final int REG_CREATED_NEW_KEY = 1;
|
|
private static final int REG_OPENED_EXISTING_KEY = 2;
|
|
private static final int NULL_NATIVE_HANDLE = 0;
|
|
|
|
/* Windows security masks */
|
|
private static final int DELETE = 0x10000;
|
|
private static final int KEY_QUERY_VALUE = 1;
|
|
private static final int KEY_SET_VALUE = 2;
|
|
private static final int KEY_CREATE_SUB_KEY = 4;
|
|
private static final int KEY_ENUMERATE_SUB_KEYS = 8;
|
|
private static final int KEY_READ = 0x20019;
|
|
private static final int KEY_WRITE = 0x20006;
|
|
private static final int KEY_ALL_ACCESS = 0xf003f;
|
|
|
|
/**
|
|
* Initial time between registry access attempts, in ms. The time is doubled
|
|
* after each failing attempt (except the first).
|
|
*/
|
|
private static int INIT_SLEEP_TIME = 50;
|
|
|
|
/**
|
|
* Maximum number of registry access attempts.
|
|
*/
|
|
private static int MAX_ATTEMPTS = 5;
|
|
|
|
/**
|
|
* BackingStore availability flag.
|
|
*/
|
|
private boolean isBackingStoreAvailable = true;
|
|
|
|
/**
|
|
* Java wrapper for Windows registry API RegOpenKey()
|
|
*/
|
|
private static native int[] WindowsRegOpenKey(int hKey, byte[] subKey,
|
|
int securityMask);
|
|
/**
|
|
* Retries RegOpenKey() MAX_ATTEMPTS times before giving up.
|
|
*/
|
|
private static int[] WindowsRegOpenKey1(int hKey, byte[] subKey,
|
|
int securityMask) {
|
|
int[] result = WindowsRegOpenKey(hKey, subKey, securityMask);
|
|
if (result[ERROR_CODE] == ERROR_SUCCESS) {
|
|
return result;
|
|
} else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) {
|
|
logger().warning("Trying to recreate Windows registry node " +
|
|
byteArrayToString(subKey) + " at root 0x" +
|
|
Integer.toHexString(hKey) + ".");
|
|
// Try recreation
|
|
int handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE];
|
|
WindowsRegCloseKey(handle);
|
|
return WindowsRegOpenKey(hKey, subKey, securityMask);
|
|
} else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) {
|
|
long sleepTime = INIT_SLEEP_TIME;
|
|
for (int i = 0; i < MAX_ATTEMPTS; i++) {
|
|
try {
|
|
Thread.sleep(sleepTime);
|
|
} catch(InterruptedException e) {
|
|
return result;
|
|
}
|
|
sleepTime *= 2;
|
|
result = WindowsRegOpenKey(hKey, subKey, securityMask);
|
|
if (result[ERROR_CODE] == ERROR_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Java wrapper for Windows registry API RegCloseKey()
|
|
*/
|
|
private static native int WindowsRegCloseKey(int hKey);
|
|
|
|
/**
|
|
* Java wrapper for Windows registry API RegCreateKeyEx()
|
|
*/
|
|
private static native int[] WindowsRegCreateKeyEx(int hKey, byte[] subKey);
|
|
|
|
/**
|
|
* Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.
|
|
*/
|
|
private static int[] WindowsRegCreateKeyEx1(int hKey, byte[] subKey) {
|
|
int[] result = WindowsRegCreateKeyEx(hKey, subKey);
|
|
if (result[ERROR_CODE] == ERROR_SUCCESS) {
|
|
return result;
|
|
} else {
|
|
long sleepTime = INIT_SLEEP_TIME;
|
|
for (int i = 0; i < MAX_ATTEMPTS; i++) {
|
|
try {
|
|
Thread.sleep(sleepTime);
|
|
} catch(InterruptedException e) {
|
|
return result;
|
|
}
|
|
sleepTime *= 2;
|
|
result = WindowsRegCreateKeyEx(hKey, subKey);
|
|
if (result[ERROR_CODE] == ERROR_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* Java wrapper for Windows registry API RegDeleteKey()
|
|
*/
|
|
private static native int WindowsRegDeleteKey(int hKey, byte[] subKey);
|
|
|
|
/**
|
|
* Java wrapper for Windows registry API RegFlushKey()
|
|
*/
|
|
private static native int WindowsRegFlushKey(int hKey);
|
|
|
|
/**
|
|
* Retries RegFlushKey() MAX_ATTEMPTS times before giving up.
|
|
*/
|
|
private static int WindowsRegFlushKey1(int hKey) {
|
|
int result = WindowsRegFlushKey(hKey);
|
|
if (result == ERROR_SUCCESS) {
|
|
return result;
|
|
} else {
|
|
long sleepTime = INIT_SLEEP_TIME;
|
|
for (int i = 0; i < MAX_ATTEMPTS; i++) {
|
|
try {
|
|
Thread.sleep(sleepTime);
|
|
} catch(InterruptedException e) {
|
|
return result;
|
|
}
|
|
sleepTime *= 2;
|
|
result = WindowsRegFlushKey(hKey);
|
|
if (result == ERROR_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Java wrapper for Windows registry API RegQueryValueEx()
|
|
*/
|
|
private static native byte[] WindowsRegQueryValueEx(int hKey,
|
|
byte[] valueName);
|
|
/**
|
|
* Java wrapper for Windows registry API RegSetValueEx()
|
|
*/
|
|
private static native int WindowsRegSetValueEx(int hKey, byte[] valueName,
|
|
byte[] value);
|
|
/**
|
|
* Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.
|
|
*/
|
|
private static int WindowsRegSetValueEx1(int hKey, byte[] valueName,
|
|
byte[] value) {
|
|
int result = WindowsRegSetValueEx(hKey, valueName, value);
|
|
if (result == ERROR_SUCCESS) {
|
|
return result;
|
|
} else {
|
|
long sleepTime = INIT_SLEEP_TIME;
|
|
for (int i = 0; i < MAX_ATTEMPTS; i++) {
|
|
try {
|
|
Thread.sleep(sleepTime);
|
|
} catch(InterruptedException e) {
|
|
return result;
|
|
}
|
|
sleepTime *= 2;
|
|
result = WindowsRegSetValueEx(hKey, valueName, value);
|
|
if (result == ERROR_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Java wrapper for Windows registry API RegDeleteValue()
|
|
*/
|
|
private static native int WindowsRegDeleteValue(int hKey, byte[] valueName);
|
|
|
|
/**
|
|
* Java wrapper for Windows registry API RegQueryInfoKey()
|
|
*/
|
|
private static native int[] WindowsRegQueryInfoKey(int hKey);
|
|
|
|
/**
|
|
* Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.
|
|
*/
|
|
private static int[] WindowsRegQueryInfoKey1(int hKey) {
|
|
int[] result = WindowsRegQueryInfoKey(hKey);
|
|
if (result[ERROR_CODE] == ERROR_SUCCESS) {
|
|
return result;
|
|
} else {
|
|
long sleepTime = INIT_SLEEP_TIME;
|
|
for (int i = 0; i < MAX_ATTEMPTS; i++) {
|
|
try {
|
|
Thread.sleep(sleepTime);
|
|
} catch(InterruptedException e) {
|
|
return result;
|
|
}
|
|
sleepTime *= 2;
|
|
result = WindowsRegQueryInfoKey(hKey);
|
|
if (result[ERROR_CODE] == ERROR_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Java wrapper for Windows registry API RegEnumKeyEx()
|
|
*/
|
|
private static native byte[] WindowsRegEnumKeyEx(int hKey, int subKeyIndex,
|
|
int maxKeyLength);
|
|
|
|
/**
|
|
* Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up.
|
|
*/
|
|
private static byte[] WindowsRegEnumKeyEx1(int hKey, int subKeyIndex,
|
|
int maxKeyLength) {
|
|
byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
|
|
if (result != null) {
|
|
return result;
|
|
} else {
|
|
long sleepTime = INIT_SLEEP_TIME;
|
|
for (int i = 0; i < MAX_ATTEMPTS; i++) {
|
|
try {
|
|
Thread.sleep(sleepTime);
|
|
} catch(InterruptedException e) {
|
|
return result;
|
|
}
|
|
sleepTime *= 2;
|
|
result = WindowsRegEnumKeyEx(hKey, subKeyIndex, maxKeyLength);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Java wrapper for Windows registry API RegEnumValue()
|
|
*/
|
|
private static native byte[] WindowsRegEnumValue(int hKey, int valueIndex,
|
|
int maxValueNameLength);
|
|
/**
|
|
* Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.
|
|
*/
|
|
private static byte[] WindowsRegEnumValue1(int hKey, int valueIndex,
|
|
int maxValueNameLength) {
|
|
byte[] result = WindowsRegEnumValue(hKey, valueIndex,
|
|
maxValueNameLength);
|
|
if (result != null) {
|
|
return result;
|
|
} else {
|
|
long sleepTime = INIT_SLEEP_TIME;
|
|
for (int i = 0; i < MAX_ATTEMPTS; i++) {
|
|
try {
|
|
Thread.sleep(sleepTime);
|
|
} catch(InterruptedException e) {
|
|
return result;
|
|
}
|
|
sleepTime *= 2;
|
|
result = WindowsRegEnumValue(hKey, valueIndex,
|
|
maxValueNameLength);
|
|
if (result != null) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Constructs a <tt>WindowsPreferences</tt> node, creating underlying
|
|
* Windows registry node and all its Windows parents, if they are not yet
|
|
* created.
|
|
* Logs a warning message, if Windows Registry is unavailable.
|
|
*/
|
|
private WindowsPreferences(WindowsPreferences parent, String name) {
|
|
super(parent, name);
|
|
int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ);
|
|
if (parentNativeHandle == NULL_NATIVE_HANDLE) {
|
|
// if here, openKey failed and logged
|
|
isBackingStoreAvailable = false;
|
|
return;
|
|
}
|
|
int[] result =
|
|
WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name));
|
|
if (result[ERROR_CODE] != ERROR_SUCCESS) {
|
|
logger().warning("Could not create windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" + Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegCreateKeyEx(...) returned error code " +
|
|
result[ERROR_CODE] + ".");
|
|
isBackingStoreAvailable = false;
|
|
return;
|
|
}
|
|
newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
|
|
closeKey(parentNativeHandle);
|
|
closeKey(result[NATIVE_HANDLE]);
|
|
}
|
|
|
|
/**
|
|
* Constructs a root node creating the underlying
|
|
* Windows registry node and all of its parents, if they have not yet been
|
|
* created.
|
|
* Logs a warning message, if Windows Registry is unavailable.
|
|
* @param rootNativeHandle Native handle to one of Windows top level keys.
|
|
* @param rootDirectory Path to root directory, as a byte-encoded string.
|
|
*/
|
|
private WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) {
|
|
super(null, "");
|
|
int[] result =
|
|
WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory);
|
|
if (result[ERROR_CODE] != ERROR_SUCCESS) {
|
|
logger().warning("Could not open/create prefs root node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" + Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegCreateKeyEx(...) returned error code " +
|
|
result[ERROR_CODE] + ".");
|
|
isBackingStoreAvailable = false;
|
|
return;
|
|
}
|
|
// Check if a new node
|
|
newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
|
|
closeKey(result[NATIVE_HANDLE]);
|
|
}
|
|
|
|
/**
|
|
* Returns Windows absolute path of the current node as a byte array.
|
|
* Java "/" separator is transformed into Windows "\".
|
|
* @see Preferences#absolutePath()
|
|
*/
|
|
private byte[] windowsAbsolutePath() {
|
|
ByteArrayOutputStream bstream = new ByteArrayOutputStream();
|
|
bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);
|
|
StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/");
|
|
while (tokenizer.hasMoreTokens()) {
|
|
bstream.write((byte)'\\');
|
|
String nextName = tokenizer.nextToken();
|
|
byte[] windowsNextName = toWindowsName(nextName);
|
|
bstream.write(windowsNextName, 0, windowsNextName.length-1);
|
|
}
|
|
bstream.write(0);
|
|
return bstream.toByteArray();
|
|
}
|
|
|
|
/**
|
|
* Opens current node's underlying Windows registry key using a
|
|
* given security mask.
|
|
* @param securityMask Windows security mask.
|
|
* @return Windows registry key's handle.
|
|
* @see #openKey(byte[], int)
|
|
* @see #openKey(int, byte[], int)
|
|
* @see #closeKey(int)
|
|
*/
|
|
private int openKey(int securityMask) {
|
|
return openKey(securityMask, securityMask);
|
|
}
|
|
|
|
/**
|
|
* Opens current node's underlying Windows registry key using a
|
|
* given security mask.
|
|
* @param mask1 Preferred Windows security mask.
|
|
* @param mask2 Alternate Windows security mask.
|
|
* @return Windows registry key's handle.
|
|
* @see #openKey(byte[], int)
|
|
* @see #openKey(int, byte[], int)
|
|
* @see #closeKey(int)
|
|
*/
|
|
private int openKey(int mask1, int mask2) {
|
|
return openKey(windowsAbsolutePath(), mask1, mask2);
|
|
}
|
|
|
|
/**
|
|
* Opens Windows registry key at a given absolute path using a given
|
|
* security mask.
|
|
* @param windowsAbsolutePath Windows absolute path of the
|
|
* key as a byte-encoded string.
|
|
* @param mask1 Preferred Windows security mask.
|
|
* @param mask2 Alternate Windows security mask.
|
|
* @return Windows registry key's handle.
|
|
* @see #openKey(int)
|
|
* @see #openKey(int, byte[],int)
|
|
* @see #closeKey(int)
|
|
*/
|
|
private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {
|
|
/* Check if key's path is short enough be opened at once
|
|
otherwise use a path-splitting procedure */
|
|
if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {
|
|
int[] result = WindowsRegOpenKey1(rootNativeHandle(),
|
|
windowsAbsolutePath, mask1);
|
|
if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
|
|
result = WindowsRegOpenKey1(rootNativeHandle(),
|
|
windowsAbsolutePath, mask2);
|
|
|
|
if (result[ERROR_CODE] != ERROR_SUCCESS) {
|
|
logger().warning("Could not open windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegOpenKey(...) returned error code " +
|
|
result[ERROR_CODE] + ".");
|
|
result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
|
|
if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {
|
|
throw new SecurityException(
|
|
"Could not open windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) +
|
|
": Access denied");
|
|
}
|
|
}
|
|
return result[NATIVE_HANDLE];
|
|
} else {
|
|
return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Opens Windows registry key at a given relative path
|
|
* with respect to a given Windows registry key.
|
|
* @param windowsAbsolutePath Windows relative path of the
|
|
* key as a byte-encoded string.
|
|
* @param nativeHandle handle to the base Windows key.
|
|
* @param mask1 Preferred Windows security mask.
|
|
* @param mask2 Alternate Windows security mask.
|
|
* @return Windows registry key's handle.
|
|
* @see #openKey(int)
|
|
* @see #openKey(byte[],int)
|
|
* @see #closeKey(int)
|
|
*/
|
|
private int openKey(int nativeHandle, byte[] windowsRelativePath,
|
|
int mask1, int mask2) {
|
|
/* If the path is short enough open at once. Otherwise split the path */
|
|
if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) {
|
|
int[] result = WindowsRegOpenKey1(nativeHandle,
|
|
windowsRelativePath, mask1);
|
|
if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
|
|
result = WindowsRegOpenKey1(nativeHandle,
|
|
windowsRelativePath, mask2);
|
|
|
|
if (result[ERROR_CODE] != ERROR_SUCCESS) {
|
|
logger().warning("Could not open windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" + Integer.toHexString(nativeHandle) +
|
|
". Windows RegOpenKey(...) returned error code " +
|
|
result[ERROR_CODE] + ".");
|
|
result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
|
|
}
|
|
return result[NATIVE_HANDLE];
|
|
} else {
|
|
int separatorPosition = -1;
|
|
// Be greedy - open the longest possible path
|
|
for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {
|
|
if (windowsRelativePath[i] == ((byte)'\\')) {
|
|
separatorPosition = i;
|
|
break;
|
|
}
|
|
}
|
|
// Split the path and do the recursion
|
|
byte[] nextRelativeRoot = new byte[separatorPosition+1];
|
|
System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0,
|
|
separatorPosition);
|
|
nextRelativeRoot[separatorPosition] = 0;
|
|
byte[] nextRelativePath = new byte[windowsRelativePath.length -
|
|
separatorPosition - 1];
|
|
System.arraycopy(windowsRelativePath, separatorPosition+1,
|
|
nextRelativePath, 0, nextRelativePath.length);
|
|
int nextNativeHandle = openKey(nativeHandle, nextRelativeRoot,
|
|
mask1, mask2);
|
|
if (nextNativeHandle == NULL_NATIVE_HANDLE) {
|
|
return NULL_NATIVE_HANDLE;
|
|
}
|
|
int result = openKey(nextNativeHandle, nextRelativePath,
|
|
mask1,mask2);
|
|
closeKey(nextNativeHandle);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes Windows registry key.
|
|
* Logs a warning if Windows registry is unavailable.
|
|
* @param key's Windows registry handle.
|
|
* @see #openKey(int)
|
|
* @see #openKey(byte[],int)
|
|
* @see #openKey(int, byte[],int)
|
|
*/
|
|
private void closeKey(int nativeHandle) {
|
|
int result = WindowsRegCloseKey(nativeHandle);
|
|
if (result != ERROR_SUCCESS) {
|
|
logger().warning("Could not close windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegCloseKey(...) returned error code " +
|
|
result + ".");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method.
|
|
* Puts name-value pair into the underlying Windows registry node.
|
|
* Logs a warning, if Windows registry is unavailable.
|
|
* @see #getSpi(String)
|
|
*/
|
|
protected void putSpi(String javaName, String value) {
|
|
int nativeHandle = openKey(KEY_SET_VALUE);
|
|
if (nativeHandle == NULL_NATIVE_HANDLE) {
|
|
isBackingStoreAvailable = false;
|
|
return;
|
|
}
|
|
int result = WindowsRegSetValueEx1(nativeHandle,
|
|
toWindowsName(javaName), toWindowsValueString(value));
|
|
if (result != ERROR_SUCCESS) {
|
|
logger().warning("Could not assign value to key " +
|
|
byteArrayToString(toWindowsName(javaName)) +
|
|
" at Windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegSetValueEx(...) returned error code " +
|
|
result + ".");
|
|
isBackingStoreAvailable = false;
|
|
}
|
|
closeKey(nativeHandle);
|
|
}
|
|
|
|
/**
|
|
* Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method.
|
|
* Gets a string value from the underlying Windows registry node.
|
|
* Logs a warning, if Windows registry is unavailable.
|
|
* @see #putSpi(String, String)
|
|
*/
|
|
protected String getSpi(String javaName) {
|
|
int nativeHandle = openKey(KEY_QUERY_VALUE);
|
|
if (nativeHandle == NULL_NATIVE_HANDLE) {
|
|
return null;
|
|
}
|
|
Object resultObject = WindowsRegQueryValueEx(nativeHandle,
|
|
toWindowsName(javaName));
|
|
if (resultObject == null) {
|
|
closeKey(nativeHandle);
|
|
return null;
|
|
}
|
|
closeKey(nativeHandle);
|
|
return toJavaValueString((byte[]) resultObject);
|
|
}
|
|
|
|
/**
|
|
* Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method.
|
|
* Deletes a string name-value pair from the underlying Windows registry
|
|
* node, if this value still exists.
|
|
* Logs a warning, if Windows registry is unavailable or key has already
|
|
* been deleted.
|
|
*/
|
|
protected void removeSpi(String key) {
|
|
int nativeHandle = openKey(KEY_SET_VALUE);
|
|
if (nativeHandle == NULL_NATIVE_HANDLE) {
|
|
return;
|
|
}
|
|
int result =
|
|
WindowsRegDeleteValue(nativeHandle, toWindowsName(key));
|
|
if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
|
|
logger().warning("Could not delete windows registry value " +
|
|
byteArrayToString(windowsAbsolutePath()) + "\\" +
|
|
toWindowsName(key) + " at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegDeleteValue(...) returned error code " +
|
|
result + ".");
|
|
isBackingStoreAvailable = false;
|
|
}
|
|
closeKey(nativeHandle);
|
|
}
|
|
|
|
/**
|
|
* Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method.
|
|
* Gets value names from the underlying Windows registry node.
|
|
* Throws a BackingStoreException and logs a warning, if
|
|
* Windows registry is unavailable.
|
|
*/
|
|
protected String[] keysSpi() throws BackingStoreException{
|
|
// Find out the number of values
|
|
int nativeHandle = openKey(KEY_QUERY_VALUE);
|
|
if (nativeHandle == NULL_NATIVE_HANDLE) {
|
|
throw new BackingStoreException(
|
|
"Could not open windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) + ".");
|
|
}
|
|
int[] result = WindowsRegQueryInfoKey1(nativeHandle);
|
|
if (result[ERROR_CODE] != ERROR_SUCCESS) {
|
|
String info = "Could not query windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegQueryInfoKeyEx(...) returned error code " +
|
|
result[ERROR_CODE] + ".";
|
|
logger().warning(info);
|
|
throw new BackingStoreException(info);
|
|
}
|
|
int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH];
|
|
int valuesNumber = result[VALUES_NUMBER];
|
|
if (valuesNumber == 0) {
|
|
closeKey(nativeHandle);
|
|
return new String[0];
|
|
}
|
|
// Get the values
|
|
String[] valueNames = new String[valuesNumber];
|
|
for (int i = 0; i < valuesNumber; i++) {
|
|
byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,
|
|
maxValueNameLength+1);
|
|
if (windowsName == null) {
|
|
String info =
|
|
"Could not enumerate value #" + i + " of windows node " +
|
|
byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) + ".";
|
|
logger().warning(info);
|
|
throw new BackingStoreException(info);
|
|
}
|
|
valueNames[i] = toJavaName(windowsName);
|
|
}
|
|
closeKey(nativeHandle);
|
|
return valueNames;
|
|
}
|
|
|
|
/**
|
|
* Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method.
|
|
* Calls Windows registry to retrive children of this node.
|
|
* Throws a BackingStoreException and logs a warning message,
|
|
* if Windows registry is not available.
|
|
*/
|
|
protected String[] childrenNamesSpi() throws BackingStoreException {
|
|
// Open key
|
|
int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE);
|
|
if (nativeHandle == NULL_NATIVE_HANDLE) {
|
|
throw new BackingStoreException(
|
|
"Could not open windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) + ".");
|
|
}
|
|
// Get number of children
|
|
int[] result = WindowsRegQueryInfoKey1(nativeHandle);
|
|
if (result[ERROR_CODE] != ERROR_SUCCESS) {
|
|
String info = "Could not query windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" + Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegQueryInfoKeyEx(...) returned error code " +
|
|
result[ERROR_CODE] + ".";
|
|
logger().warning(info);
|
|
throw new BackingStoreException(info);
|
|
}
|
|
int maxKeyLength = result[MAX_KEY_LENGTH];
|
|
int subKeysNumber = result[SUBKEYS_NUMBER];
|
|
if (subKeysNumber == 0) {
|
|
closeKey(nativeHandle);
|
|
return new String[0];
|
|
}
|
|
String[] subkeys = new String[subKeysNumber];
|
|
String[] children = new String[subKeysNumber];
|
|
// Get children
|
|
for (int i = 0; i < subKeysNumber; i++) {
|
|
byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,
|
|
maxKeyLength+1);
|
|
if (windowsName == null) {
|
|
String info =
|
|
"Could not enumerate key #" + i + " of windows node " +
|
|
byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) + ". ";
|
|
logger().warning(info);
|
|
throw new BackingStoreException(info);
|
|
}
|
|
String javaName = toJavaName(windowsName);
|
|
children[i] = javaName;
|
|
}
|
|
closeKey(nativeHandle);
|
|
return children;
|
|
}
|
|
|
|
/**
|
|
* Implements <tt>Preferences</tt> <tt>flush()</tt> method.
|
|
* Flushes Windows registry changes to disk.
|
|
* Throws a BackingStoreException and logs a warning message if Windows
|
|
* registry is not available.
|
|
*/
|
|
public void flush() throws BackingStoreException{
|
|
|
|
if (isRemoved()) {
|
|
parent.flush();
|
|
return;
|
|
}
|
|
if (!isBackingStoreAvailable) {
|
|
throw new BackingStoreException(
|
|
"flush(): Backing store not available.");
|
|
}
|
|
int nativeHandle = openKey(KEY_READ);
|
|
if (nativeHandle == NULL_NATIVE_HANDLE) {
|
|
throw new BackingStoreException(
|
|
"Could not open windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) + ".");
|
|
}
|
|
int result = WindowsRegFlushKey1(nativeHandle);
|
|
if (result != ERROR_SUCCESS) {
|
|
String info = "Could not flush windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegFlushKey(...) returned error code " +
|
|
result + ".";
|
|
logger().warning(info);
|
|
throw new BackingStoreException(info);
|
|
}
|
|
closeKey(nativeHandle);
|
|
}
|
|
|
|
|
|
/**
|
|
* Implements <tt>Preferences</tt> <tt>sync()</tt> method.
|
|
* Flushes Windows registry changes to disk. Equivalent to flush().
|
|
* @see flush()
|
|
*/
|
|
public void sync() throws BackingStoreException{
|
|
if (isRemoved())
|
|
throw new IllegalStateException("Node has been removed");
|
|
flush();
|
|
}
|
|
|
|
/**
|
|
* Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method.
|
|
* Constructs a child node with a
|
|
* given name and creates its underlying Windows registry node,
|
|
* if it does not exist.
|
|
* Logs a warning message, if Windows Registry is unavailable.
|
|
*/
|
|
protected AbstractPreferences childSpi(String name) {
|
|
return new WindowsPreferences(this, name);
|
|
}
|
|
|
|
/**
|
|
* Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method.
|
|
* Deletes underlying Windows registry node.
|
|
* Throws a BackingStoreException and logs a warning, if Windows registry
|
|
* is not available.
|
|
*/
|
|
public void removeNodeSpi() throws BackingStoreException {
|
|
int parentNativeHandle =
|
|
((WindowsPreferences)parent()).openKey(DELETE);
|
|
if (parentNativeHandle == NULL_NATIVE_HANDLE) {
|
|
throw new BackingStoreException(
|
|
"Could not open parent windows registry node of " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" +
|
|
Integer.toHexString(rootNativeHandle()) + ".");
|
|
}
|
|
int result =
|
|
WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name()));
|
|
if (result != ERROR_SUCCESS) {
|
|
String info = "Could not delete windows registry node " +
|
|
byteArrayToString(windowsAbsolutePath()) +
|
|
" at root 0x" + Integer.toHexString(rootNativeHandle()) +
|
|
". Windows RegDeleteKeyEx(...) returned error code " +
|
|
result + ".";
|
|
logger().warning(info);
|
|
throw new BackingStoreException(info);
|
|
}
|
|
closeKey(parentNativeHandle);
|
|
}
|
|
|
|
/**
|
|
* Converts value's or node's name from its byte array representation to
|
|
* java string. Two encodings, simple and altBase64 are used. See
|
|
* {@link #toWindowsName(String) toWindowsName()} for a detailed
|
|
* description of encoding conventions.
|
|
* @param windowsNameArray Null-terminated byte array.
|
|
*/
|
|
private static String toJavaName(byte[] windowsNameArray) {
|
|
String windowsName = byteArrayToString(windowsNameArray);
|
|
// check if Alt64
|
|
if ((windowsName.length() > 1) &&
|
|
(windowsName.substring(0, 2).equals("/!"))) {
|
|
return toJavaAlt64Name(windowsName);
|
|
}
|
|
StringBuilder javaName = new StringBuilder();
|
|
char ch;
|
|
// Decode from simple encoding
|
|
for (int i = 0; i < windowsName.length(); i++) {
|
|
if ((ch = windowsName.charAt(i)) == '/') {
|
|
char next = ' ';
|
|
if ((windowsName.length() > i + 1) &&
|
|
((next = windowsName.charAt(i+1)) >= 'A') &&
|
|
(next <= 'Z')) {
|
|
ch = next;
|
|
i++;
|
|
} else if ((windowsName.length() > i + 1) &&
|
|
(next == '/')) {
|
|
ch = '\\';
|
|
i++;
|
|
}
|
|
} else if (ch == '\\') {
|
|
ch = '/';
|
|
}
|
|
javaName.append(ch);
|
|
}
|
|
return javaName.toString();
|
|
}
|
|
|
|
/**
|
|
* Converts value's or node's name from its Windows representation to java
|
|
* string, using altBase64 encoding. See
|
|
* {@link #toWindowsName(String) toWindowsName()} for a detailed
|
|
* description of encoding conventions.
|
|
*/
|
|
|
|
private static String toJavaAlt64Name(String windowsName) {
|
|
byte[] byteBuffer =
|
|
Base64.altBase64ToByteArray(windowsName.substring(2));
|
|
StringBuilder result = new StringBuilder();
|
|
for (int i = 0; i < byteBuffer.length; i++) {
|
|
int firstbyte = (byteBuffer[i++] & 0xff);
|
|
int secondbyte = (byteBuffer[i] & 0xff);
|
|
result.append((char)((firstbyte << 8) + secondbyte));
|
|
}
|
|
return result.toString();
|
|
}
|
|
|
|
/**
|
|
* Converts value's or node's name to its Windows representation
|
|
* as a byte-encoded string.
|
|
* Two encodings, simple and altBase64 are used.
|
|
* <p>
|
|
* <i>Simple</i> encoding is used, if java string does not contain
|
|
* any characters less, than 0x0020, or greater, than 0x007f.
|
|
* Simple encoding adds "/" character to capital letters, i.e.
|
|
* "A" is encoded as "/A". Character '\' is encoded as '//',
|
|
* '/' is encoded as '\'.
|
|
* The constructed string is converted to byte array by truncating the
|
|
* highest byte and adding the terminating <tt>null</tt> character.
|
|
* <p>
|
|
* <i>altBase64</i> encoding is used, if java string does contain at least
|
|
* one character less, than 0x0020, or greater, than 0x007f.
|
|
* This encoding is marked by setting first two bytes of the
|
|
* Windows string to '/!'. The java name is then encoded using
|
|
* byteArrayToAltBase64() method from
|
|
* Base64 class.
|
|
*/
|
|
private static byte[] toWindowsName(String javaName) {
|
|
StringBuilder windowsName = new StringBuilder();
|
|
for (int i = 0; i < javaName.length(); i++) {
|
|
char ch = javaName.charAt(i);
|
|
if ((ch < 0x0020) || (ch > 0x007f)) {
|
|
// If a non-trivial character encountered, use altBase64
|
|
return toWindowsAlt64Name(javaName);
|
|
}
|
|
if (ch == '\\') {
|
|
windowsName.append("//");
|
|
} else if (ch == '/') {
|
|
windowsName.append('\\');
|
|
} else if ((ch >= 'A') && (ch <='Z')) {
|
|
windowsName.append('/').append(ch);
|
|
} else {
|
|
windowsName.append(ch);
|
|
}
|
|
}
|
|
return stringToByteArray(windowsName.toString());
|
|
}
|
|
|
|
/**
|
|
* Converts value's or node's name to its Windows representation
|
|
* as a byte-encoded string, using altBase64 encoding. See
|
|
* {@link #toWindowsName(String) toWindowsName()} for a detailed
|
|
* description of encoding conventions.
|
|
*/
|
|
private static byte[] toWindowsAlt64Name(String javaName) {
|
|
byte[] javaNameArray = new byte[2*javaName.length()];
|
|
// Convert to byte pairs
|
|
int counter = 0;
|
|
for (int i = 0; i < javaName.length();i++) {
|
|
int ch = javaName.charAt(i);
|
|
javaNameArray[counter++] = (byte)(ch >>> 8);
|
|
javaNameArray[counter++] = (byte)ch;
|
|
}
|
|
|
|
return stringToByteArray("/!" +
|
|
Base64.byteArrayToAltBase64(javaNameArray));
|
|
}
|
|
|
|
/**
|
|
* Converts value string from its Windows representation
|
|
* to java string. See
|
|
* {@link #toWindowsValueString(String) toWindowsValueString()} for the
|
|
* description of the encoding algorithm.
|
|
*/
|
|
private static String toJavaValueString(byte[] windowsNameArray) {
|
|
// Use modified native2ascii algorithm
|
|
String windowsName = byteArrayToString(windowsNameArray);
|
|
StringBuilder javaName = new StringBuilder();
|
|
char ch;
|
|
for (int i = 0; i < windowsName.length(); i++){
|
|
if ((ch = windowsName.charAt(i)) == '/') {
|
|
char next = ' ';
|
|
|
|
if (windowsName.length() > i + 1 &&
|
|
(next = windowsName.charAt(i + 1)) == 'u') {
|
|
if (windowsName.length() < i + 6) {
|
|
break;
|
|
} else {
|
|
ch = (char)Integer.parseInt(
|
|
windowsName.substring(i + 2, i + 6), 16);
|
|
i += 5;
|
|
}
|
|
} else
|
|
if ((windowsName.length() > i + 1) &&
|
|
((windowsName.charAt(i+1)) >= 'A') &&
|
|
(next <= 'Z')) {
|
|
ch = next;
|
|
i++;
|
|
} else if ((windowsName.length() > i + 1) &&
|
|
(next == '/')) {
|
|
ch = '\\';
|
|
i++;
|
|
}
|
|
} else if (ch == '\\') {
|
|
ch = '/';
|
|
}
|
|
javaName.append(ch);
|
|
}
|
|
return javaName.toString();
|
|
}
|
|
|
|
/**
|
|
* Converts value string to it Windows representation.
|
|
* as a byte-encoded string.
|
|
* Encoding algorithm adds "/" character to capital letters, i.e.
|
|
* "A" is encoded as "/A". Character '\' is encoded as '//',
|
|
* '/' is encoded as '\'.
|
|
* Then encoding scheme similar to jdk's native2ascii converter is used
|
|
* to convert java string to a byte array of ASCII characters.
|
|
*/
|
|
private static byte[] toWindowsValueString(String javaName) {
|
|
StringBuilder windowsName = new StringBuilder();
|
|
for (int i = 0; i < javaName.length(); i++) {
|
|
char ch = javaName.charAt(i);
|
|
if ((ch < 0x0020) || (ch > 0x007f)){
|
|
// write \udddd
|
|
windowsName.append("/u");
|
|
String hex = Integer.toHexString(javaName.charAt(i));
|
|
StringBuilder hex4 = new StringBuilder(hex);
|
|
hex4.reverse();
|
|
int len = 4 - hex4.length();
|
|
for (int j = 0; j < len; j++){
|
|
hex4.append('0');
|
|
}
|
|
for (int j = 0; j < 4; j++){
|
|
windowsName.append(hex4.charAt(3 - j));
|
|
}
|
|
} else if (ch == '\\') {
|
|
windowsName.append("//");
|
|
} else if (ch == '/') {
|
|
windowsName.append('\\');
|
|
} else if ((ch >= 'A') && (ch <='Z')) {
|
|
windowsName.append('/').append(ch);
|
|
} else {
|
|
windowsName.append(ch);
|
|
}
|
|
}
|
|
return stringToByteArray(windowsName.toString());
|
|
}
|
|
|
|
/**
|
|
* Returns native handle for the top Windows node for this node.
|
|
*/
|
|
private int rootNativeHandle() {
|
|
return (isUserNode()
|
|
? USER_ROOT_NATIVE_HANDLE
|
|
: SYSTEM_ROOT_NATIVE_HANDLE);
|
|
}
|
|
|
|
/**
|
|
* Returns this java string as a null-terminated byte array
|
|
*/
|
|
private static byte[] stringToByteArray(String str) {
|
|
byte[] result = new byte[str.length()+1];
|
|
for (int i = 0; i < str.length(); i++) {
|
|
result[i] = (byte) str.charAt(i);
|
|
}
|
|
result[str.length()] = 0;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Converts a null-terminated byte array to java string
|
|
*/
|
|
private static String byteArrayToString(byte[] array) {
|
|
StringBuilder result = new StringBuilder();
|
|
for (int i = 0; i < array.length - 1; i++) {
|
|
result.append((char)array[i]);
|
|
}
|
|
return result.toString();
|
|
}
|
|
|
|
/**
|
|
* Empty, never used implementation of AbstractPreferences.flushSpi().
|
|
*/
|
|
protected void flushSpi() throws BackingStoreException {
|
|
// assert false;
|
|
}
|
|
|
|
/**
|
|
* Empty, never used implementation of AbstractPreferences.flushSpi().
|
|
*/
|
|
protected void syncSpi() throws BackingStoreException {
|
|
// assert false;
|
|
}
|
|
|
|
private static synchronized PlatformLogger logger() {
|
|
if (logger == null) {
|
|
logger = PlatformLogger.getLogger("java.util.prefs");
|
|
}
|
|
return logger;
|
|
}
|
|
}
|