0001: /*
0002: * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package java.util.prefs;
0027:
0028: import java.util.Map;
0029: import java.util.TreeMap;
0030: import java.util.StringTokenizer;
0031: import java.io.ByteArrayOutputStream;
0032: import java.util.logging.Logger;
0033:
0034: /**
0035: * Windows registry based implementation of <tt>Preferences</tt>.
0036: * <tt>Preferences</tt>' <tt>systemRoot</tt> and <tt>userRoot</tt> are stored in
0037: * <tt>HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs</tt> and
0038: * <tt>HKEY_CURRENT_USER\Software\JavaSoft\Prefs</tt> correspondingly.
0039: *
0040: * @author Konstantin Kladko
0041: * @version 1.24, 05/05/07
0042: * @see Preferences
0043: * @see PreferencesFactory
0044: * @since 1.4
0045: */
0046:
0047: class WindowsPreferences extends AbstractPreferences {
0048:
0049: /**
0050: * Logger for error messages
0051: */
0052: private static Logger logger;
0053:
0054: /**
0055: * Windows registry path to <tt>Preferences</tt>'s root nodes.
0056: */
0057: private static final byte[] WINDOWS_ROOT_PATH = stringToByteArray("Software\\JavaSoft\\Prefs");
0058:
0059: /**
0060: * Windows handles to <tt>HKEY_CURRENT_USER</tt> and
0061: * <tt>HKEY_LOCAL_MACHINE</tt> hives.
0062: */
0063: private static final int HKEY_CURRENT_USER = 0x80000001;
0064: private static final int HKEY_LOCAL_MACHINE = 0x80000002;
0065:
0066: /**
0067: * Mount point for <tt>Preferences</tt>' user root.
0068: */
0069: private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER;
0070:
0071: /**
0072: * Mount point for <tt>Preferences</tt>' system root.
0073: */
0074: private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE;
0075:
0076: /**
0077: * Maximum byte-encoded path length for Windows native functions,
0078: * ending <tt>null</tt> character not included.
0079: */
0080: private static final int MAX_WINDOWS_PATH_LENGTH = 256;
0081:
0082: /**
0083: * User root node.
0084: */
0085: static final Preferences userRoot = new WindowsPreferences(
0086: USER_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
0087:
0088: /**
0089: * System root node.
0090: */
0091: static final Preferences systemRoot = new WindowsPreferences(
0092: SYSTEM_ROOT_NATIVE_HANDLE, WINDOWS_ROOT_PATH);
0093:
0094: /* Windows error codes. */
0095: private static final int ERROR_SUCCESS = 0;
0096: private static final int ERROR_FILE_NOT_FOUND = 2;
0097: private static final int ERROR_ACCESS_DENIED = 5;
0098:
0099: /* Constants used to interpret returns of native functions */
0100: private static final int NATIVE_HANDLE = 0;
0101: private static final int ERROR_CODE = 1;
0102: private static final int SUBKEYS_NUMBER = 0;
0103: private static final int VALUES_NUMBER = 2;
0104: private static final int MAX_KEY_LENGTH = 3;
0105: private static final int MAX_VALUE_NAME_LENGTH = 4;
0106: private static final int DISPOSITION = 2;
0107: private static final int REG_CREATED_NEW_KEY = 1;
0108: private static final int REG_OPENED_EXISTING_KEY = 2;
0109: private static final int NULL_NATIVE_HANDLE = 0;
0110:
0111: /* Windows security masks */
0112: private static final int DELETE = 0x10000;
0113: private static final int KEY_QUERY_VALUE = 1;
0114: private static final int KEY_SET_VALUE = 2;
0115: private static final int KEY_CREATE_SUB_KEY = 4;
0116: private static final int KEY_ENUMERATE_SUB_KEYS = 8;
0117: private static final int KEY_READ = 0x20019;
0118: private static final int KEY_WRITE = 0x20006;
0119: private static final int KEY_ALL_ACCESS = 0xf003f;
0120:
0121: /**
0122: * Initial time between registry access attempts, in ms. The time is doubled
0123: * after each failing attempt (except the first).
0124: */
0125: private static int INIT_SLEEP_TIME = 50;
0126:
0127: /**
0128: * Maximum number of registry access attempts.
0129: */
0130: private static int MAX_ATTEMPTS = 5;
0131:
0132: /**
0133: * BackingStore availability flag.
0134: */
0135: private boolean isBackingStoreAvailable = true;
0136:
0137: /**
0138: * Java wrapper for Windows registry API RegOpenKey()
0139: */
0140: private static native int[] WindowsRegOpenKey(int hKey,
0141: byte[] subKey, int securityMask);
0142:
0143: /**
0144: * Retries RegOpenKey() MAX_ATTEMPTS times before giving up.
0145: */
0146: private static int[] WindowsRegOpenKey1(int hKey, byte[] subKey,
0147: int securityMask) {
0148: int[] result = WindowsRegOpenKey(hKey, subKey, securityMask);
0149: if (result[ERROR_CODE] == ERROR_SUCCESS) {
0150: return result;
0151: } else if (result[ERROR_CODE] == ERROR_FILE_NOT_FOUND) {
0152: logger().warning(
0153: "Trying to recreate Windows registry node "
0154: + byteArrayToString(subKey) + " at root 0x"
0155: + Integer.toHexString(hKey) + ".");
0156: // Try recreation
0157: int handle = WindowsRegCreateKeyEx(hKey, subKey)[NATIVE_HANDLE];
0158: WindowsRegCloseKey(handle);
0159: return WindowsRegOpenKey(hKey, subKey, securityMask);
0160: } else if (result[ERROR_CODE] != ERROR_ACCESS_DENIED) {
0161: long sleepTime = INIT_SLEEP_TIME;
0162: for (int i = 0; i < MAX_ATTEMPTS; i++) {
0163: try {
0164: Thread.sleep(sleepTime);
0165: } catch (InterruptedException e) {
0166: return result;
0167: }
0168: sleepTime *= 2;
0169: result = WindowsRegOpenKey(hKey, subKey, securityMask);
0170: if (result[ERROR_CODE] == ERROR_SUCCESS) {
0171: return result;
0172: }
0173: }
0174: }
0175: return result;
0176: }
0177:
0178: /**
0179: * Java wrapper for Windows registry API RegCloseKey()
0180: */
0181: private static native int WindowsRegCloseKey(int hKey);
0182:
0183: /**
0184: * Java wrapper for Windows registry API RegCreateKeyEx()
0185: */
0186: private static native int[] WindowsRegCreateKeyEx(int hKey,
0187: byte[] subKey);
0188:
0189: /**
0190: * Retries RegCreateKeyEx() MAX_ATTEMPTS times before giving up.
0191: */
0192: private static int[] WindowsRegCreateKeyEx1(int hKey, byte[] subKey) {
0193: int[] result = WindowsRegCreateKeyEx(hKey, subKey);
0194: if (result[ERROR_CODE] == ERROR_SUCCESS) {
0195: return result;
0196: } else {
0197: long sleepTime = INIT_SLEEP_TIME;
0198: for (int i = 0; i < MAX_ATTEMPTS; i++) {
0199: try {
0200: Thread.sleep(sleepTime);
0201: } catch (InterruptedException e) {
0202: return result;
0203: }
0204: sleepTime *= 2;
0205: result = WindowsRegCreateKeyEx(hKey, subKey);
0206: if (result[ERROR_CODE] == ERROR_SUCCESS) {
0207: return result;
0208: }
0209: }
0210: }
0211: return result;
0212: }
0213:
0214: /**
0215: * Java wrapper for Windows registry API RegDeleteKey()
0216: */
0217: private static native int WindowsRegDeleteKey(int hKey,
0218: byte[] subKey);
0219:
0220: /**
0221: * Java wrapper for Windows registry API RegFlushKey()
0222: */
0223: private static native int WindowsRegFlushKey(int hKey);
0224:
0225: /**
0226: * Retries RegFlushKey() MAX_ATTEMPTS times before giving up.
0227: */
0228: private static int WindowsRegFlushKey1(int hKey) {
0229: int result = WindowsRegFlushKey(hKey);
0230: if (result == ERROR_SUCCESS) {
0231: return result;
0232: } else {
0233: long sleepTime = INIT_SLEEP_TIME;
0234: for (int i = 0; i < MAX_ATTEMPTS; i++) {
0235: try {
0236: Thread.sleep(sleepTime);
0237: } catch (InterruptedException e) {
0238: return result;
0239: }
0240: sleepTime *= 2;
0241: result = WindowsRegFlushKey(hKey);
0242: if (result == ERROR_SUCCESS) {
0243: return result;
0244: }
0245: }
0246: }
0247: return result;
0248: }
0249:
0250: /**
0251: * Java wrapper for Windows registry API RegQueryValueEx()
0252: */
0253: private static native byte[] WindowsRegQueryValueEx(int hKey,
0254: byte[] valueName);
0255:
0256: /**
0257: * Java wrapper for Windows registry API RegSetValueEx()
0258: */
0259: private static native int WindowsRegSetValueEx(int hKey,
0260: byte[] valueName, byte[] value);
0261:
0262: /**
0263: * Retries RegSetValueEx() MAX_ATTEMPTS times before giving up.
0264: */
0265: private static int WindowsRegSetValueEx1(int hKey,
0266: byte[] valueName, byte[] value) {
0267: int result = WindowsRegSetValueEx(hKey, valueName, value);
0268: if (result == ERROR_SUCCESS) {
0269: return result;
0270: } else {
0271: long sleepTime = INIT_SLEEP_TIME;
0272: for (int i = 0; i < MAX_ATTEMPTS; i++) {
0273: try {
0274: Thread.sleep(sleepTime);
0275: } catch (InterruptedException e) {
0276: return result;
0277: }
0278: sleepTime *= 2;
0279: result = WindowsRegSetValueEx(hKey, valueName, value);
0280: if (result == ERROR_SUCCESS) {
0281: return result;
0282: }
0283: }
0284: }
0285: return result;
0286: }
0287:
0288: /**
0289: * Java wrapper for Windows registry API RegDeleteValue()
0290: */
0291: private static native int WindowsRegDeleteValue(int hKey,
0292: byte[] valueName);
0293:
0294: /**
0295: * Java wrapper for Windows registry API RegQueryInfoKey()
0296: */
0297: private static native int[] WindowsRegQueryInfoKey(int hKey);
0298:
0299: /**
0300: * Retries RegQueryInfoKey() MAX_ATTEMPTS times before giving up.
0301: */
0302: private static int[] WindowsRegQueryInfoKey1(int hKey) {
0303: int[] result = WindowsRegQueryInfoKey(hKey);
0304: if (result[ERROR_CODE] == ERROR_SUCCESS) {
0305: return result;
0306: } else {
0307: long sleepTime = INIT_SLEEP_TIME;
0308: for (int i = 0; i < MAX_ATTEMPTS; i++) {
0309: try {
0310: Thread.sleep(sleepTime);
0311: } catch (InterruptedException e) {
0312: return result;
0313: }
0314: sleepTime *= 2;
0315: result = WindowsRegQueryInfoKey(hKey);
0316: if (result[ERROR_CODE] == ERROR_SUCCESS) {
0317: return result;
0318: }
0319: }
0320: }
0321: return result;
0322: }
0323:
0324: /**
0325: * Java wrapper for Windows registry API RegEnumKeyEx()
0326: */
0327: private static native byte[] WindowsRegEnumKeyEx(int hKey,
0328: int subKeyIndex, int maxKeyLength);
0329:
0330: /**
0331: * Retries RegEnumKeyEx() MAX_ATTEMPTS times before giving up.
0332: */
0333: private static byte[] WindowsRegEnumKeyEx1(int hKey,
0334: int subKeyIndex, int maxKeyLength) {
0335: byte[] result = WindowsRegEnumKeyEx(hKey, subKeyIndex,
0336: maxKeyLength);
0337: if (result != null) {
0338: return result;
0339: } else {
0340: long sleepTime = INIT_SLEEP_TIME;
0341: for (int i = 0; i < MAX_ATTEMPTS; i++) {
0342: try {
0343: Thread.sleep(sleepTime);
0344: } catch (InterruptedException e) {
0345: return result;
0346: }
0347: sleepTime *= 2;
0348: result = WindowsRegEnumKeyEx(hKey, subKeyIndex,
0349: maxKeyLength);
0350: if (result != null) {
0351: return result;
0352: }
0353: }
0354: }
0355: return result;
0356: }
0357:
0358: /**
0359: * Java wrapper for Windows registry API RegEnumValue()
0360: */
0361: private static native byte[] WindowsRegEnumValue(int hKey,
0362: int valueIndex, int maxValueNameLength);
0363:
0364: /**
0365: * Retries RegEnumValueEx() MAX_ATTEMPTS times before giving up.
0366: */
0367: private static byte[] WindowsRegEnumValue1(int hKey,
0368: int valueIndex, int maxValueNameLength) {
0369: byte[] result = WindowsRegEnumValue(hKey, valueIndex,
0370: maxValueNameLength);
0371: if (result != null) {
0372: return result;
0373: } else {
0374: long sleepTime = INIT_SLEEP_TIME;
0375: for (int i = 0; i < MAX_ATTEMPTS; i++) {
0376: try {
0377: Thread.sleep(sleepTime);
0378: } catch (InterruptedException e) {
0379: return result;
0380: }
0381: sleepTime *= 2;
0382: result = WindowsRegEnumValue(hKey, valueIndex,
0383: maxValueNameLength);
0384: if (result != null) {
0385: return result;
0386: }
0387: }
0388: }
0389: return result;
0390: }
0391:
0392: /**
0393: * Constructs a <tt>WindowsPreferences</tt> node, creating underlying
0394: * Windows registry node and all its Windows parents, if they are not yet
0395: * created.
0396: * Logs a warning message, if Windows Registry is unavailable.
0397: */
0398: private WindowsPreferences(WindowsPreferences parent, String name) {
0399: super (parent, name);
0400: int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY,
0401: KEY_READ);
0402: if (parentNativeHandle == NULL_NATIVE_HANDLE) {
0403: // if here, openKey failed and logged
0404: isBackingStoreAvailable = false;
0405: return;
0406: }
0407: int[] result = WindowsRegCreateKeyEx1(parentNativeHandle,
0408: toWindowsName(name));
0409: if (result[ERROR_CODE] != ERROR_SUCCESS) {
0410: logger()
0411: .warning(
0412: "Could not create windows registry "
0413: + "node "
0414: + byteArrayToString(windowsAbsolutePath())
0415: + " at root 0x"
0416: + Integer
0417: .toHexString(rootNativeHandle())
0418: + ". Windows RegCreateKeyEx(...) returned error code "
0419: + result[ERROR_CODE] + ".");
0420: isBackingStoreAvailable = false;
0421: return;
0422: }
0423: newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
0424: closeKey(parentNativeHandle);
0425: closeKey(result[NATIVE_HANDLE]);
0426: }
0427:
0428: /**
0429: * Constructs a root node creating the underlying
0430: * Windows registry node and all of its parents, if they have not yet been
0431: * created.
0432: * Logs a warning message, if Windows Registry is unavailable.
0433: * @param rootNativeHandle Native handle to one of Windows top level keys.
0434: * @param rootDirectory Path to root directory, as a byte-encoded string.
0435: */
0436: private WindowsPreferences(int rootNativeHandle,
0437: byte[] rootDirectory) {
0438: super (null, "");
0439: int[] result = WindowsRegCreateKeyEx1(rootNativeHandle,
0440: rootDirectory);
0441: if (result[ERROR_CODE] != ERROR_SUCCESS) {
0442: logger()
0443: .warning(
0444: "Could not open/create prefs root node "
0445: + byteArrayToString(windowsAbsolutePath())
0446: + " at root 0x"
0447: + Integer
0448: .toHexString(rootNativeHandle())
0449: + ". Windows RegCreateKeyEx(...) returned error code "
0450: + result[ERROR_CODE] + ".");
0451: isBackingStoreAvailable = false;
0452: return;
0453: }
0454: // Check if a new node
0455: newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
0456: closeKey(result[NATIVE_HANDLE]);
0457: }
0458:
0459: /**
0460: * Returns Windows absolute path of the current node as a byte array.
0461: * Java "/" separator is transformed into Windows "\".
0462: * @see Preferences#absolutePath()
0463: */
0464: private byte[] windowsAbsolutePath() {
0465: ByteArrayOutputStream bstream = new ByteArrayOutputStream();
0466: bstream.write(WINDOWS_ROOT_PATH, 0,
0467: WINDOWS_ROOT_PATH.length - 1);
0468: StringTokenizer tokenizer = new StringTokenizer(absolutePath(),
0469: "/");
0470: while (tokenizer.hasMoreTokens()) {
0471: bstream.write((byte) '\\');
0472: String nextName = tokenizer.nextToken();
0473: byte[] windowsNextName = toWindowsName(nextName);
0474: bstream.write(windowsNextName, 0,
0475: windowsNextName.length - 1);
0476: }
0477: bstream.write(0);
0478: return bstream.toByteArray();
0479: }
0480:
0481: /**
0482: * Opens current node's underlying Windows registry key using a
0483: * given security mask.
0484: * @param securityMask Windows security mask.
0485: * @return Windows registry key's handle.
0486: * @see #openKey(byte[], int)
0487: * @see #openKey(int, byte[], int)
0488: * @see #closeKey(int)
0489: */
0490: private int openKey(int securityMask) {
0491: return openKey(securityMask, securityMask);
0492: }
0493:
0494: /**
0495: * Opens current node's underlying Windows registry key using a
0496: * given security mask.
0497: * @param mask1 Preferred Windows security mask.
0498: * @param mask2 Alternate Windows security mask.
0499: * @return Windows registry key's handle.
0500: * @see #openKey(byte[], int)
0501: * @see #openKey(int, byte[], int)
0502: * @see #closeKey(int)
0503: */
0504: private int openKey(int mask1, int mask2) {
0505: return openKey(windowsAbsolutePath(), mask1, mask2);
0506: }
0507:
0508: /**
0509: * Opens Windows registry key at a given absolute path using a given
0510: * security mask.
0511: * @param windowsAbsolutePath Windows absolute path of the
0512: * key as a byte-encoded string.
0513: * @param mask1 Preferred Windows security mask.
0514: * @param mask2 Alternate Windows security mask.
0515: * @return Windows registry key's handle.
0516: * @see #openKey(int)
0517: * @see #openKey(int, byte[],int)
0518: * @see #closeKey(int)
0519: */
0520: private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {
0521: /* Check if key's path is short enough be opened at once
0522: otherwise use a path-splitting procedure */
0523: if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {
0524: int[] result = WindowsRegOpenKey1(rootNativeHandle(),
0525: windowsAbsolutePath, mask1);
0526: if (result[ERROR_CODE] == ERROR_ACCESS_DENIED
0527: && mask2 != mask1)
0528: result = WindowsRegOpenKey1(rootNativeHandle(),
0529: windowsAbsolutePath, mask2);
0530:
0531: if (result[ERROR_CODE] != ERROR_SUCCESS) {
0532: logger()
0533: .warning(
0534: "Could not open windows "
0535: + "registry node "
0536: + byteArrayToString(windowsAbsolutePath())
0537: + " at root 0x"
0538: + Integer
0539: .toHexString(rootNativeHandle())
0540: + ". Windows RegOpenKey(...) returned error code "
0541: + result[ERROR_CODE] + ".");
0542: result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
0543: if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {
0544: throw new SecurityException(
0545: "Could not open windows "
0546: + "registry node "
0547: + byteArrayToString(windowsAbsolutePath())
0548: + " at root 0x"
0549: + Integer
0550: .toHexString(rootNativeHandle())
0551: + ": Access denied");
0552: }
0553: }
0554: return result[NATIVE_HANDLE];
0555: } else {
0556: return openKey(rootNativeHandle(), windowsAbsolutePath,
0557: mask1, mask2);
0558: }
0559: }
0560:
0561: /**
0562: * Opens Windows registry key at a given relative path
0563: * with respect to a given Windows registry key.
0564: * @param windowsAbsolutePath Windows relative path of the
0565: * key as a byte-encoded string.
0566: * @param nativeHandle handle to the base Windows key.
0567: * @param mask1 Preferred Windows security mask.
0568: * @param mask2 Alternate Windows security mask.
0569: * @return Windows registry key's handle.
0570: * @see #openKey(int)
0571: * @see #openKey(byte[],int)
0572: * @see #closeKey(int)
0573: */
0574: private int openKey(int nativeHandle, byte[] windowsRelativePath,
0575: int mask1, int mask2) {
0576: /* If the path is short enough open at once. Otherwise split the path */
0577: if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {
0578: int[] result = WindowsRegOpenKey1(nativeHandle,
0579: windowsRelativePath, mask1);
0580: if (result[ERROR_CODE] == ERROR_ACCESS_DENIED
0581: && mask2 != mask1)
0582: result = WindowsRegOpenKey1(nativeHandle,
0583: windowsRelativePath, mask2);
0584:
0585: if (result[ERROR_CODE] != ERROR_SUCCESS) {
0586: logger()
0587: .warning(
0588: "Could not open windows "
0589: + "registry node "
0590: + byteArrayToString(windowsAbsolutePath())
0591: + " at root 0x"
0592: + Integer
0593: .toHexString(nativeHandle)
0594: + ". Windows RegOpenKey(...) returned error code "
0595: + result[ERROR_CODE] + ".");
0596: result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
0597: }
0598: return result[NATIVE_HANDLE];
0599: } else {
0600: int separatorPosition = -1;
0601: // Be greedy - open the longest possible path
0602: for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {
0603: if (windowsRelativePath[i] == ((byte) '\\')) {
0604: separatorPosition = i;
0605: break;
0606: }
0607: }
0608: // Split the path and do the recursion
0609: byte[] nextRelativeRoot = new byte[separatorPosition + 1];
0610: System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,
0611: 0, separatorPosition);
0612: nextRelativeRoot[separatorPosition] = 0;
0613: byte[] nextRelativePath = new byte[windowsRelativePath.length
0614: - separatorPosition - 1];
0615: System.arraycopy(windowsRelativePath,
0616: separatorPosition + 1, nextRelativePath, 0,
0617: nextRelativePath.length);
0618: int nextNativeHandle = openKey(nativeHandle,
0619: nextRelativeRoot, mask1, mask2);
0620: if (nextNativeHandle == NULL_NATIVE_HANDLE) {
0621: return NULL_NATIVE_HANDLE;
0622: }
0623: int result = openKey(nextNativeHandle, nextRelativePath,
0624: mask1, mask2);
0625: closeKey(nextNativeHandle);
0626: return result;
0627: }
0628: }
0629:
0630: /**
0631: * Closes Windows registry key.
0632: * Logs a warning if Windows registry is unavailable.
0633: * @param key's Windows registry handle.
0634: * @see #openKey(int)
0635: * @see #openKey(byte[],int)
0636: * @see #openKey(int, byte[],int)
0637: */
0638: private void closeKey(int nativeHandle) {
0639: int result = WindowsRegCloseKey(nativeHandle);
0640: if (result != ERROR_SUCCESS) {
0641: logger()
0642: .warning(
0643: "Could not close windows "
0644: + "registry node "
0645: + byteArrayToString(windowsAbsolutePath())
0646: + " at root 0x"
0647: + Integer
0648: .toHexString(rootNativeHandle())
0649: + ". Windows RegCloseKey(...) returned error code "
0650: + result + ".");
0651: }
0652: }
0653:
0654: /**
0655: * Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method.
0656: * Puts name-value pair into the underlying Windows registry node.
0657: * Logs a warning, if Windows registry is unavailable.
0658: * @see #getSpi(String)
0659: */
0660: protected void putSpi(String javaName, String value) {
0661: int nativeHandle = openKey(KEY_SET_VALUE);
0662: if (nativeHandle == NULL_NATIVE_HANDLE) {
0663: isBackingStoreAvailable = false;
0664: return;
0665: }
0666: int result = WindowsRegSetValueEx1(nativeHandle,
0667: toWindowsName(javaName), toWindowsValueString(value));
0668: if (result != ERROR_SUCCESS) {
0669: logger()
0670: .warning(
0671: "Could not assign value to key "
0672: + byteArrayToString(toWindowsName(javaName))
0673: + " at Windows registry node "
0674: + byteArrayToString(windowsAbsolutePath())
0675: + " at root 0x"
0676: + Integer
0677: .toHexString(rootNativeHandle())
0678: + ". Windows RegSetValueEx(...) returned error code "
0679: + result + ".");
0680: isBackingStoreAvailable = false;
0681: }
0682: closeKey(nativeHandle);
0683: }
0684:
0685: /**
0686: * Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method.
0687: * Gets a string value from the underlying Windows registry node.
0688: * Logs a warning, if Windows registry is unavailable.
0689: * @see #putSpi(String, String)
0690: */
0691: protected String getSpi(String javaName) {
0692: int nativeHandle = openKey(KEY_QUERY_VALUE);
0693: if (nativeHandle == NULL_NATIVE_HANDLE) {
0694: return null;
0695: }
0696: Object resultObject = WindowsRegQueryValueEx(nativeHandle,
0697: toWindowsName(javaName));
0698: if (resultObject == null) {
0699: closeKey(nativeHandle);
0700: return null;
0701: }
0702: closeKey(nativeHandle);
0703: return toJavaValueString((byte[]) resultObject);
0704: }
0705:
0706: /**
0707: * Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method.
0708: * Deletes a string name-value pair from the underlying Windows registry
0709: * node, if this value still exists.
0710: * Logs a warning, if Windows registry is unavailable or key has already
0711: * been deleted.
0712: */
0713: protected void removeSpi(String key) {
0714: int nativeHandle = openKey(KEY_SET_VALUE);
0715: if (nativeHandle == NULL_NATIVE_HANDLE) {
0716: return;
0717: }
0718: int result = WindowsRegDeleteValue(nativeHandle,
0719: toWindowsName(key));
0720: if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
0721: logger()
0722: .warning(
0723: "Could not delete windows registry "
0724: + "value "
0725: + byteArrayToString(windowsAbsolutePath())
0726: + "\\"
0727: + toWindowsName(key)
0728: + " at root 0x"
0729: + Integer
0730: .toHexString(rootNativeHandle())
0731: + ". Windows RegDeleteValue(...) returned error code "
0732: + result + ".");
0733: isBackingStoreAvailable = false;
0734: }
0735: closeKey(nativeHandle);
0736: }
0737:
0738: /**
0739: * Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method.
0740: * Gets value names from the underlying Windows registry node.
0741: * Throws a BackingStoreException and logs a warning, if
0742: * Windows registry is unavailable.
0743: */
0744: protected String[] keysSpi() throws BackingStoreException {
0745: // Find out the number of values
0746: int nativeHandle = openKey(KEY_QUERY_VALUE);
0747: if (nativeHandle == NULL_NATIVE_HANDLE) {
0748: throw new BackingStoreException("Could not open windows"
0749: + "registry node "
0750: + byteArrayToString(windowsAbsolutePath())
0751: + " at root 0x"
0752: + Integer.toHexString(rootNativeHandle()) + ".");
0753: }
0754: int[] result = WindowsRegQueryInfoKey1(nativeHandle);
0755: if (result[ERROR_CODE] != ERROR_SUCCESS) {
0756: String info = "Could not query windows"
0757: + "registry node "
0758: + byteArrayToString(windowsAbsolutePath())
0759: + " at root 0x"
0760: + Integer.toHexString(rootNativeHandle())
0761: + ". Windows RegQueryInfoKeyEx(...) returned error code "
0762: + result[ERROR_CODE] + ".";
0763: logger().warning(info);
0764: throw new BackingStoreException(info);
0765: }
0766: int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH];
0767: int valuesNumber = result[VALUES_NUMBER];
0768: if (valuesNumber == 0) {
0769: closeKey(nativeHandle);
0770: return new String[0];
0771: }
0772: // Get the values
0773: String[] valueNames = new String[valuesNumber];
0774: for (int i = 0; i < valuesNumber; i++) {
0775: byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,
0776: maxValueNameLength + 1);
0777: if (windowsName == null) {
0778: String info = "Could not enumerate value #" + i
0779: + " of windows node "
0780: + byteArrayToString(windowsAbsolutePath())
0781: + " at root 0x"
0782: + Integer.toHexString(rootNativeHandle()) + ".";
0783: logger().warning(info);
0784: throw new BackingStoreException(info);
0785: }
0786: valueNames[i] = toJavaName(windowsName);
0787: }
0788: closeKey(nativeHandle);
0789: return valueNames;
0790: }
0791:
0792: /**
0793: * Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method.
0794: * Calls Windows registry to retrive children of this node.
0795: * Throws a BackingStoreException and logs a warning message,
0796: * if Windows registry is not available.
0797: */
0798: protected String[] childrenNamesSpi() throws BackingStoreException {
0799: // Open key
0800: int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS
0801: | KEY_QUERY_VALUE);
0802: if (nativeHandle == NULL_NATIVE_HANDLE) {
0803: throw new BackingStoreException("Could not open windows"
0804: + "registry node "
0805: + byteArrayToString(windowsAbsolutePath())
0806: + " at root 0x"
0807: + Integer.toHexString(rootNativeHandle()) + ".");
0808: }
0809: // Get number of children
0810: int[] result = WindowsRegQueryInfoKey1(nativeHandle);
0811: if (result[ERROR_CODE] != ERROR_SUCCESS) {
0812: String info = "Could not query windows"
0813: + "registry node "
0814: + byteArrayToString(windowsAbsolutePath())
0815: + " at root 0x"
0816: + Integer.toHexString(rootNativeHandle())
0817: + ". Windows RegQueryInfoKeyEx(...) returned error code "
0818: + result[ERROR_CODE] + ".";
0819: logger().warning(info);
0820: throw new BackingStoreException(info);
0821: }
0822: int maxKeyLength = result[MAX_KEY_LENGTH];
0823: int subKeysNumber = result[SUBKEYS_NUMBER];
0824: if (subKeysNumber == 0) {
0825: closeKey(nativeHandle);
0826: return new String[0];
0827: }
0828: String[] subkeys = new String[subKeysNumber];
0829: String[] children = new String[subKeysNumber];
0830: // Get children
0831: for (int i = 0; i < subKeysNumber; i++) {
0832: byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,
0833: maxKeyLength + 1);
0834: if (windowsName == null) {
0835: String info = "Could not enumerate key #" + i
0836: + " of windows node "
0837: + byteArrayToString(windowsAbsolutePath())
0838: + " at root 0x"
0839: + Integer.toHexString(rootNativeHandle())
0840: + ". ";
0841: logger().warning(info);
0842: throw new BackingStoreException(info);
0843: }
0844: String javaName = toJavaName(windowsName);
0845: children[i] = javaName;
0846: }
0847: closeKey(nativeHandle);
0848: return children;
0849: }
0850:
0851: /**
0852: * Implements <tt>Preferences</tt> <tt>flush()</tt> method.
0853: * Flushes Windows registry changes to disk.
0854: * Throws a BackingStoreException and logs a warning message if Windows
0855: * registry is not available.
0856: */
0857: public void flush() throws BackingStoreException {
0858:
0859: if (isRemoved()) {
0860: parent.flush();
0861: return;
0862: }
0863: if (!isBackingStoreAvailable) {
0864: throw new BackingStoreException(
0865: "flush(): Backing store not available.");
0866: }
0867: int nativeHandle = openKey(KEY_READ);
0868: if (nativeHandle == NULL_NATIVE_HANDLE) {
0869: throw new BackingStoreException("Could not open windows"
0870: + "registry node "
0871: + byteArrayToString(windowsAbsolutePath())
0872: + " at root 0x"
0873: + Integer.toHexString(rootNativeHandle()) + ".");
0874: }
0875: int result = WindowsRegFlushKey1(nativeHandle);
0876: if (result != ERROR_SUCCESS) {
0877: String info = "Could not flush windows " + "registry node "
0878: + byteArrayToString(windowsAbsolutePath())
0879: + " at root 0x"
0880: + Integer.toHexString(rootNativeHandle())
0881: + ". Windows RegFlushKey(...) returned error code "
0882: + result + ".";
0883: logger().warning(info);
0884: throw new BackingStoreException(info);
0885: }
0886: closeKey(nativeHandle);
0887: }
0888:
0889: /**
0890: * Implements <tt>Preferences</tt> <tt>sync()</tt> method.
0891: * Flushes Windows registry changes to disk. Equivalent to flush().
0892: * @see flush()
0893: */
0894: public void sync() throws BackingStoreException {
0895: if (isRemoved())
0896: throw new IllegalStateException("Node has been removed");
0897: flush();
0898: }
0899:
0900: /**
0901: * Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method.
0902: * Constructs a child node with a
0903: * given name and creates its underlying Windows registry node,
0904: * if it does not exist.
0905: * Logs a warning message, if Windows Registry is unavailable.
0906: */
0907: protected AbstractPreferences childSpi(String name) {
0908: return new WindowsPreferences(this , name);
0909: }
0910:
0911: /**
0912: * Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method.
0913: * Deletes underlying Windows registry node.
0914: * Throws a BackingStoreException and logs a warning, if Windows registry
0915: * is not available.
0916: */
0917: public void removeNodeSpi() throws BackingStoreException {
0918: int parentNativeHandle = ((WindowsPreferences) parent())
0919: .openKey(DELETE);
0920: if (parentNativeHandle == NULL_NATIVE_HANDLE) {
0921: throw new BackingStoreException(
0922: "Could not open parent windows"
0923: + "registry node of "
0924: + byteArrayToString(windowsAbsolutePath())
0925: + " at root 0x"
0926: + Integer.toHexString(rootNativeHandle())
0927: + ".");
0928: }
0929: int result = WindowsRegDeleteKey(parentNativeHandle,
0930: toWindowsName(name()));
0931: if (result != ERROR_SUCCESS) {
0932: String info = "Could not delete windows "
0933: + "registry node "
0934: + byteArrayToString(windowsAbsolutePath())
0935: + " at root 0x"
0936: + Integer.toHexString(rootNativeHandle())
0937: + ". Windows RegDeleteKeyEx(...) returned error code "
0938: + result + ".";
0939: logger().warning(info);
0940: throw new BackingStoreException(info);
0941: }
0942: closeKey(parentNativeHandle);
0943: }
0944:
0945: /**
0946: * Converts value's or node's name from its byte array representation to
0947: * java string. Two encodings, simple and altBase64 are used. See
0948: * {@link #toWindowsName(String) toWindowsName()} for a detailed
0949: * description of encoding conventions.
0950: * @param windowsNameArray Null-terminated byte array.
0951: */
0952: private static String toJavaName(byte[] windowsNameArray) {
0953: String windowsName = byteArrayToString(windowsNameArray);
0954: // check if Alt64
0955: if ((windowsName.length() > 1)
0956: && (windowsName.substring(0, 2).equals("/!"))) {
0957: return toJavaAlt64Name(windowsName);
0958: }
0959: StringBuffer javaName = new StringBuffer();
0960: char ch;
0961: // Decode from simple encoding
0962: for (int i = 0; i < windowsName.length(); i++) {
0963: if ((ch = windowsName.charAt(i)) == '/') {
0964: char next = ' ';
0965: if ((windowsName.length() > i + 1)
0966: && ((next = windowsName.charAt(i + 1)) >= 'A')
0967: && (next <= 'Z')) {
0968: ch = next;
0969: i++;
0970: } else if ((windowsName.length() > i + 1)
0971: && (next == '/')) {
0972: ch = '\\';
0973: i++;
0974: }
0975: } else if (ch == '\\') {
0976: ch = '/';
0977: }
0978: javaName.append(ch);
0979: }
0980: return javaName.toString();
0981: }
0982:
0983: /**
0984: * Converts value's or node's name from its Windows representation to java
0985: * string, using altBase64 encoding. See
0986: * {@link #toWindowsName(String) toWindowsName()} for a detailed
0987: * description of encoding conventions.
0988: */
0989:
0990: private static String toJavaAlt64Name(String windowsName) {
0991: byte[] byteBuffer = Base64.altBase64ToByteArray(windowsName
0992: .substring(2));
0993: StringBuffer result = new StringBuffer();
0994: for (int i = 0; i < byteBuffer.length; i++) {
0995: int firstbyte = (byteBuffer[i++] & 0xff);
0996: int secondbyte = (byteBuffer[i] & 0xff);
0997: result.append((char) ((firstbyte << 8) + secondbyte));
0998: }
0999: return result.toString();
1000: }
1001:
1002: /**
1003: * Converts value's or node's name to its Windows representation
1004: * as a byte-encoded string.
1005: * Two encodings, simple and altBase64 are used.
1006: * <p>
1007: * <i>Simple</i> encoding is used, if java string does not contain
1008: * any characters less, than 0x0020, or greater, than 0x007f.
1009: * Simple encoding adds "/" character to capital letters, i.e.
1010: * "A" is encoded as "/A". Character '\' is encoded as '//',
1011: * '/' is encoded as '\'.
1012: * The constructed string is converted to byte array by truncating the
1013: * highest byte and adding the terminating <tt>null</tt> character.
1014: * <p>
1015: * <i>altBase64</i> encoding is used, if java string does contain at least
1016: * one character less, than 0x0020, or greater, than 0x007f.
1017: * This encoding is marked by setting first two bytes of the
1018: * Windows string to '/!'. The java name is then encoded using
1019: * byteArrayToAltBase64() method from
1020: * Base64 class.
1021: */
1022: private static byte[] toWindowsName(String javaName) {
1023: StringBuffer windowsName = new StringBuffer();
1024: for (int i = 0; i < javaName.length(); i++) {
1025: char ch = javaName.charAt(i);
1026: if ((ch < 0x0020) || (ch > 0x007f)) {
1027: // If a non-trivial character encountered, use altBase64
1028: return toWindowsAlt64Name(javaName);
1029: }
1030: if (ch == '\\') {
1031: windowsName.append("//");
1032: } else if (ch == '/') {
1033: windowsName.append('\\');
1034: } else if ((ch >= 'A') && (ch <= 'Z')) {
1035: windowsName.append("/" + ch);
1036: } else {
1037: windowsName.append(ch);
1038: }
1039: }
1040: return stringToByteArray(windowsName.toString());
1041: }
1042:
1043: /**
1044: * Converts value's or node's name to its Windows representation
1045: * as a byte-encoded string, using altBase64 encoding. See
1046: * {@link #toWindowsName(String) toWindowsName()} for a detailed
1047: * description of encoding conventions.
1048: */
1049: private static byte[] toWindowsAlt64Name(String javaName) {
1050: byte[] javaNameArray = new byte[2 * javaName.length()];
1051: // Convert to byte pairs
1052: int counter = 0;
1053: for (int i = 0; i < javaName.length(); i++) {
1054: int ch = javaName.charAt(i);
1055: javaNameArray[counter++] = (byte) (ch >>> 8);
1056: javaNameArray[counter++] = (byte) ch;
1057: }
1058:
1059: return stringToByteArray("/!"
1060: + Base64.byteArrayToAltBase64(javaNameArray));
1061: }
1062:
1063: /**
1064: * Converts value string from its Windows representation
1065: * to java string. See
1066: * {@link #toWindowsValueString(String) toWindowsValueString()} for the
1067: * description of the encoding algorithm.
1068: */
1069: private static String toJavaValueString(byte[] windowsNameArray) {
1070: // Use modified native2ascii algorithm
1071: String windowsName = byteArrayToString(windowsNameArray);
1072: StringBuffer javaName = new StringBuffer();
1073: char ch;
1074: for (int i = 0; i < windowsName.length(); i++) {
1075: if ((ch = windowsName.charAt(i)) == '/') {
1076: char next = ' ';
1077:
1078: if (windowsName.length() > i + 1
1079: && (next = windowsName.charAt(i + 1)) == 'u') {
1080: if (windowsName.length() < i + 6) {
1081: break;
1082: } else {
1083: ch = (char) Integer.parseInt(windowsName
1084: .substring(i + 2, i + 6), 16);
1085: i += 5;
1086: }
1087: } else if ((windowsName.length() > i + 1)
1088: && ((windowsName.charAt(i + 1)) >= 'A')
1089: && (next <= 'Z')) {
1090: ch = next;
1091: i++;
1092: } else if ((windowsName.length() > i + 1)
1093: && (next == '/')) {
1094: ch = '\\';
1095: i++;
1096: }
1097: } else if (ch == '\\') {
1098: ch = '/';
1099: }
1100: javaName.append(ch);
1101: }
1102: return javaName.toString();
1103: }
1104:
1105: /**
1106: * Converts value string to it Windows representation.
1107: * as a byte-encoded string.
1108: * Encoding algorithm adds "/" character to capital letters, i.e.
1109: * "A" is encoded as "/A". Character '\' is encoded as '//',
1110: * '/' is encoded as '\'.
1111: * Then encoding scheme similar to jdk's native2ascii converter is used
1112: * to convert java string to a byte array of ASCII characters.
1113: */
1114: private static byte[] toWindowsValueString(String javaName) {
1115: StringBuffer windowsName = new StringBuffer();
1116: for (int i = 0; i < javaName.length(); i++) {
1117: char ch = javaName.charAt(i);
1118: if ((ch < 0x0020) || (ch > 0x007f)) {
1119: // write \udddd
1120: windowsName.append("/u");
1121: String hex = Integer.toHexString(javaName.charAt(i));
1122: StringBuffer hex4 = new StringBuffer(hex);
1123: hex4.reverse();
1124: int len = 4 - hex4.length();
1125: for (int j = 0; j < len; j++) {
1126: hex4.append('0');
1127: }
1128: for (int j = 0; j < 4; j++) {
1129: windowsName.append(hex4.charAt(3 - j));
1130: }
1131: } else if (ch == '\\') {
1132: windowsName.append("//");
1133: } else if (ch == '/') {
1134: windowsName.append('\\');
1135: } else if ((ch >= 'A') && (ch <= 'Z')) {
1136: windowsName.append("/" + ch);
1137: } else {
1138: windowsName.append(ch);
1139: }
1140: }
1141: return stringToByteArray(windowsName.toString());
1142: }
1143:
1144: /**
1145: * Returns native handle for the top Windows node for this node.
1146: */
1147: private int rootNativeHandle() {
1148: return (isUserNode() ? USER_ROOT_NATIVE_HANDLE
1149: : SYSTEM_ROOT_NATIVE_HANDLE);
1150: }
1151:
1152: /**
1153: * Returns this java string as a null-terminated byte array
1154: */
1155: private static byte[] stringToByteArray(String str) {
1156: byte[] result = new byte[str.length() + 1];
1157: for (int i = 0; i < str.length(); i++) {
1158: result[i] = (byte) str.charAt(i);
1159: }
1160: result[str.length()] = 0;
1161: return result;
1162: }
1163:
1164: /**
1165: * Converts a null-terminated byte array to java string
1166: */
1167: private static String byteArrayToString(byte[] array) {
1168: StringBuffer result = new StringBuffer();
1169: for (int i = 0; i < array.length - 1; i++) {
1170: result.append((char) array[i]);
1171: }
1172: return result.toString();
1173: }
1174:
1175: /**
1176: * Empty, never used implementation of AbstractPreferences.flushSpi().
1177: */
1178: protected void flushSpi() throws BackingStoreException {
1179: // assert false;
1180: }
1181:
1182: /**
1183: * Empty, never used implementation of AbstractPreferences.flushSpi().
1184: */
1185: protected void syncSpi() throws BackingStoreException {
1186: // assert false;
1187: }
1188:
1189: private static synchronized Logger logger() {
1190: if (logger == null) {
1191: logger = Logger.getLogger("java.util.prefs");
1192: }
1193: return logger;
1194: }
1195: }
|