undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,278 @@
/*
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
import java.util.Objects;
class MacOSXPreferences extends AbstractPreferences {
// CF preferences file name for Java nodes with short names
// This value is also in MacOSXPreferencesFile.c
private static final String defaultAppName = "com.apple.java.util.prefs";
// true if this node is a child of userRoot or is userRoot
private final boolean isUser;
// CF's storage location for this node and its keys
private final MacOSXPreferencesFile file;
// absolutePath() + "/"
private final String path;
// User root and system root nodes
private static volatile MacOSXPreferences userRoot;
private static volatile MacOSXPreferences systemRoot;
// Returns user root node, creating it if necessary.
// Called by MacOSXPreferencesFactory
static Preferences getUserRoot() {
MacOSXPreferences root = userRoot;
if (root == null) {
synchronized (MacOSXPreferences.class) {
root = userRoot;
if (root == null) {
userRoot = root = new MacOSXPreferences(true);
}
}
}
return root;
}
// Returns system root node, creating it if necessary.
// Called by MacOSXPreferencesFactory
static Preferences getSystemRoot() {
MacOSXPreferences root = systemRoot;
if (root == null) {
synchronized (MacOSXPreferences.class) {
root = systemRoot;
if (root == null) {
systemRoot = root = new MacOSXPreferences(false);
}
}
}
return root;
}
// Create a new root node. Called by getUserRoot() and getSystemRoot()
// Synchronization is provided by the caller.
private MacOSXPreferences(boolean newIsUser) {
this(null, "", false, true, newIsUser);
}
// Create a new non-root node with the given parent.
// Called by childSpi().
private MacOSXPreferences(MacOSXPreferences parent, String name) {
this(parent, name, false, false, false);
}
private MacOSXPreferences(MacOSXPreferences parent, String name,
boolean isNew)
{
this(parent, name, isNew, false, false);
}
private MacOSXPreferences(MacOSXPreferences parent, String name,
boolean isNew, boolean isRoot, boolean isUser)
{
super(parent, name);
if (isRoot)
this.isUser = isUser;
else
this.isUser = isUserNode();
path = isRoot ? absolutePath() : absolutePath() + "/";
file = cfFileForNode(this.isUser);
if (isNew)
newNode = isNew;
else
newNode = file.addNode(path);
}
// Create and return the MacOSXPreferencesFile for this node.
// Does not write anything to the file.
private MacOSXPreferencesFile cfFileForNode(boolean isUser)
{
String name = path;
// /one/two/three/four/five/
// The fourth slash is the end of the first three components.
// If there is no fourth slash, the name has fewer than 3 components
int pos = -1;
for (int i = 0; i < 4; i++) {
pos = name.indexOf('/', pos+1);
if (pos == -1) break;
}
if (pos == -1) {
// fewer than three components - use default name
name = defaultAppName;
} else {
// truncate to three components, no leading or trailing '/'
// replace '/' with '.' to make filesystem happy
// convert to all lowercase to survive on HFS+
name = name.substring(1, pos);
name = name.replace('/', '.');
name = name.toLowerCase();
}
return MacOSXPreferencesFile.getFile(name, isUser);
}
// AbstractPreferences implementation
@Override
protected void putSpi(String key, String value)
{
file.addKeyToNode(path, key, value);
}
// AbstractPreferences implementation
@Override
protected String getSpi(String key)
{
return file.getKeyFromNode(path, key);
}
// AbstractPreferences implementation
@Override
protected void removeSpi(String key)
{
Objects.requireNonNull(key, "Specified key cannot be null");
file.removeKeyFromNode(path, key);
}
// AbstractPreferences implementation
@Override
protected void removeNodeSpi()
throws BackingStoreException
{
// Disallow flush or sync between these two operations
// (they may be manipulating two different files)
synchronized(MacOSXPreferencesFile.class) {
((MacOSXPreferences)parent()).removeChild(name());
file.removeNode(path);
}
}
// Erase knowledge about a child of this node. Called by removeNodeSpi.
private void removeChild(String child)
{
file.removeChildFromNode(path, child);
}
// AbstractPreferences implementation
@Override
protected String[] childrenNamesSpi()
throws BackingStoreException
{
String[] result = file.getChildrenForNode(path);
if (result == null) throw new BackingStoreException("Couldn't get list of children for node '" + path + "'");
return result;
}
// AbstractPreferences implementation
@Override
protected String[] keysSpi()
throws BackingStoreException
{
String[] result = file.getKeysForNode(path);
if (result == null) throw new BackingStoreException("Couldn't get list of keys for node '" + path + "'");
return result;
}
// AbstractPreferences implementation
@Override
protected AbstractPreferences childSpi(String name)
{
// Add to parent's child list here and disallow sync
// because parent and child might be in different files.
synchronized(MacOSXPreferencesFile.class) {
boolean isNew = file.addChildToNode(path, name);
return new MacOSXPreferences(this, name, isNew);
}
}
// AbstractPreferences override
@Override
public void flush()
throws BackingStoreException
{
// Flush should *not* check for removal, unlike sync, but should
// prevent simultaneous removal.
synchronized(lock) {
if (isUser) {
if (!MacOSXPreferencesFile.flushUser()) {
throw new BackingStoreException("Synchronization failed for node '" + path + "'");
}
} else {
if (!MacOSXPreferencesFile.flushWorld()) {
throw new BackingStoreException("Synchronization failed for node '" + path + "'");
}
}
}
}
// AbstractPreferences implementation
@Override
protected void flushSpi()
throws BackingStoreException
{
// nothing here - overridden flush() doesn't call this
}
// AbstractPreferences override
@Override
public void sync()
throws BackingStoreException
{
synchronized(lock) {
if (isRemoved())
throw new IllegalStateException("Node has been removed");
// fixme! overkill
if (isUser) {
if (!MacOSXPreferencesFile.syncUser()) {
throw new BackingStoreException("Synchronization failed for node '" + path + "'");
}
} else {
if (!MacOSXPreferencesFile.syncWorld()) {
throw new BackingStoreException("Synchronization failed for node '" + path + "'");
}
}
}
}
// AbstractPreferences implementation
@Override
protected void syncSpi()
throws BackingStoreException
{
// nothing here - overridden sync() doesn't call this
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
class MacOSXPreferencesFactory implements PreferencesFactory {
@Override
public Preferences userRoot() {
return MacOSXPreferences.getUserRoot();
}
@Override
public Preferences systemRoot() {
return MacOSXPreferences.getSystemRoot();
}
}

View file

@ -0,0 +1,480 @@
/*
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.lang.ref.WeakReference;
/*
MacOSXPreferencesFile synchronization:
Everything is synchronized on MacOSXPreferencesFile.class. This prevents:
* simultaneous updates to cachedFiles or changedFiles
* simultaneous creation of two objects for the same name+user+host triplet
* simultaneous modifications to the same file
* modifications during syncWorld/flushWorld
* (in MacOSXPreferences.removeNodeSpi()) modification or sync during
multi-step node removal process
... among other things.
*/
/*
Timers. There are two timers that control synchronization of prefs data to
and from disk.
* Sync timer periodically calls syncWorld() to force external disk changes
(e.g. from another VM) into the memory cache. The sync timer runs even
if there are no outstanding local changes. The sync timer syncs all live
MacOSXPreferencesFile objects (the cachedFiles list).
The sync timer period is controlled by the java.util.prefs.syncInterval
property (same as FileSystemPreferences). By default there is *no*
sync timer (unlike FileSystemPreferences); it is only enabled if the
syncInterval property is set. The minimum interval is 5 seconds.
* Flush timer calls flushWorld() to force local changes to disk.
The flush timer is scheduled to fire some time after each pref change,
unless it's already scheduled to fire before that. syncWorld and
flushWorld will cancel any outstanding flush timer as unnecessary.
The flush timer flushes all changed files (the changedFiles list).
The time between pref write and flush timer call is controlled by the
java.util.prefs.flushDelay property (unlike FileSystemPreferences).
The default is 60 seconds and the minimum is 5 seconds.
The flush timer's behavior is required by the Java Preferences spec
("changes will eventually propagate to the persistent backing store with
an implementation-dependent delay"). The sync timer is not required by
the spec (multiple VMs are only required to not corrupt the prefs), but
the periodic sync is implemented by FileSystemPreferences and may be
useful to some programs. The sync timer is disabled by default because
it's expensive and is usually not necessary.
*/
class MacOSXPreferencesFile {
static {
loadPrefsLib();
}
@SuppressWarnings("restricted")
private static void loadPrefsLib() {
System.loadLibrary("prefs");
}
private static class FlushTask extends TimerTask {
public void run() {
MacOSXPreferencesFile.flushWorld();
}
}
// Maps string -> weak reference to MacOSXPreferencesFile
private static HashMap<String, WeakReference<MacOSXPreferencesFile>>
cachedFiles;
// Files that may have unflushed changes
private static HashSet<MacOSXPreferencesFile> changedFiles;
// Timer and pending sync and flush tasks (which are both scheduled
// on the same timer)
private static Timer timer = null;
private static FlushTask flushTimerTask = null;
private static long flushDelay = -1; // in seconds (min 5, default 60)
private static long syncInterval = -1; // (min 5, default negative == off)
private String appName;
private long user;
private long host;
String name() { return appName; }
long user() { return user; }
long host() { return host; }
// private constructor - use factory method getFile() instead
private MacOSXPreferencesFile(String newName, long newUser, long newHost)
{
appName = newName;
user = newUser;
host = newHost;
}
// Factory method
// Always returns the same object for the given name+user+host
static synchronized MacOSXPreferencesFile
getFile(String newName, boolean isUser)
{
MacOSXPreferencesFile result = null;
if (cachedFiles == null)
cachedFiles = new HashMap<>();
String hashkey =
newName + String.valueOf(isUser);
WeakReference<MacOSXPreferencesFile> hashvalue = cachedFiles.get(hashkey);
if (hashvalue != null) {
result = hashvalue.get();
}
if (result == null) {
// Java user node == CF current user, any host
// Java system node == CF any user, current host
result = new MacOSXPreferencesFile(newName,
isUser ? cfCurrentUser : cfAnyUser,
isUser ? cfAnyHost : cfCurrentHost);
cachedFiles.put(hashkey, new WeakReference<MacOSXPreferencesFile>(result));
}
// Don't schedule this file for flushing until some nodes or
// keys are added to it.
// Do set up the sync timer if requested; sync timer affects reads
// as well as writes.
initSyncTimerIfNeeded();
return result;
}
// Write all prefs changes to disk and clear all cached prefs values
// (so the next read will read from disk).
static synchronized boolean syncWorld()
{
boolean ok = true;
if (cachedFiles != null && !cachedFiles.isEmpty()) {
Iterator<WeakReference<MacOSXPreferencesFile>> iter =
cachedFiles.values().iterator();
while (iter.hasNext()) {
WeakReference<MacOSXPreferencesFile> ref = iter.next();
MacOSXPreferencesFile f = ref.get();
if (f != null) {
if (!f.synchronize()) ok = false;
} else {
iter.remove();
}
}
}
// Kill any pending flush
if (flushTimerTask != null) {
flushTimerTask.cancel();
flushTimerTask = null;
}
// Clear changed file list. The changed files were guaranteed to
// have been in the cached file list (because there was a strong
// reference from changedFiles.
if (changedFiles != null) changedFiles.clear();
return ok;
}
// Sync only current user preferences
static synchronized boolean syncUser() {
boolean ok = true;
if (cachedFiles != null && !cachedFiles.isEmpty()) {
Iterator<WeakReference<MacOSXPreferencesFile>> iter =
cachedFiles.values().iterator();
while (iter.hasNext()) {
WeakReference<MacOSXPreferencesFile> ref = iter.next();
MacOSXPreferencesFile f = ref.get();
if (f != null && f.user == cfCurrentUser) {
if (!f.synchronize()) {
ok = false;
}
} else {
iter.remove();
}
}
}
// Remove synchronized file from changed file list. The changed files were
// guaranteed to have been in the cached file list (because there was a strong
// reference from changedFiles.
if (changedFiles != null) {
Iterator<MacOSXPreferencesFile> iterChanged = changedFiles.iterator();
while (iterChanged.hasNext()) {
MacOSXPreferencesFile f = iterChanged.next();
if (f != null && f.user == cfCurrentUser)
iterChanged.remove();
}
}
return ok;
}
//Flush only current user preferences
static synchronized boolean flushUser() {
boolean ok = true;
if (changedFiles != null && !changedFiles.isEmpty()) {
Iterator<MacOSXPreferencesFile> iterator = changedFiles.iterator();
while(iterator.hasNext()) {
MacOSXPreferencesFile f = iterator.next();
if (f.user == cfCurrentUser) {
if (!f.synchronize())
ok = false;
else
iterator.remove();
}
}
}
return ok;
}
// Write all prefs changes to disk, but do not clear all cached prefs
// values. Also kills any scheduled flush task.
// There's no CFPreferencesFlush() (<rdar://problem/3049129>), so lots of cached prefs
// are cleared anyway.
static synchronized boolean flushWorld()
{
boolean ok = true;
if (changedFiles != null && !changedFiles.isEmpty()) {
for (MacOSXPreferencesFile f : changedFiles) {
if (!f.synchronize())
ok = false;
}
changedFiles.clear();
}
if (flushTimerTask != null) {
flushTimerTask.cancel();
flushTimerTask = null;
}
return ok;
}
// Mark this prefs file as changed. The changes will be flushed in
// at most flushDelay() seconds.
// Must be called when synchronized on MacOSXPreferencesFile.class
private void markChanged()
{
// Add this file to the changed file list
if (changedFiles == null)
changedFiles = new HashSet<>();
changedFiles.add(this);
// Schedule a new flush and a shutdown hook, if necessary
if (flushTimerTask == null) {
flushTimerTask = new FlushTask();
timer().schedule(flushTimerTask, flushDelay() * 1000);
}
}
// Return the flush delay, initializing from a property if necessary.
private static synchronized long flushDelay()
{
if (flushDelay == -1) {
try {
// flush delay >= 5, default 60
flushDelay = Math.max(5, Integer.parseInt(System.getProperty("java.util.prefs.flushDelay", "60")));
} catch (NumberFormatException e) {
flushDelay = 60;
}
}
return flushDelay;
}
// Initialize and run the sync timer, if the sync timer property is set
// and the sync timer hasn't already been started.
private static synchronized void initSyncTimerIfNeeded()
{
// syncInterval: -1 is uninitialized, other negative is off,
// positive is seconds between syncs (min 5).
if (syncInterval == -1) {
try {
syncInterval = Integer.parseInt(System.getProperty("java.util.prefs.syncInterval", "-2"));
if (syncInterval >= 0) {
// minimum of 5 seconds
syncInterval = Math.max(5, syncInterval);
} else {
syncInterval = -2; // default off
}
} catch (NumberFormatException e) {
syncInterval = -2; // bad property value - default off
}
if (syncInterval > 0) {
timer().schedule(new TimerTask() {
@Override
public void run() {
MacOSXPreferencesFile.syncWorld();}
}, syncInterval * 1000, syncInterval * 1000);
} else {
// syncInterval property not set. No sync timer ever.
}
}
}
// Return the timer used for flush and sync, creating it if necessary.
private static synchronized Timer timer()
{
if (timer == null) {
timer = new Timer(true); // daemon
Thread flushThread =
new Thread(null, null, "Flush Thread", 0, false) {
@Override
public void run() {
flushWorld();
}
};
/* Set context class loader to null in order to avoid
* keeping a strong reference to an application classloader.
*/
flushThread.setContextClassLoader(null);
Runtime.getRuntime().addShutdownHook(flushThread);
}
return timer;
}
// Node manipulation
boolean addNode(String path)
{
synchronized(MacOSXPreferencesFile.class) {
markChanged();
return addNode(path, appName, user, host);
}
}
void removeNode(String path)
{
synchronized(MacOSXPreferencesFile.class) {
markChanged();
removeNode(path, appName, user, host);
}
}
boolean addChildToNode(String path, String child)
{
synchronized(MacOSXPreferencesFile.class) {
markChanged();
return addChildToNode(path, child+"/", appName, user, host);
}
}
void removeChildFromNode(String path, String child)
{
synchronized(MacOSXPreferencesFile.class) {
markChanged();
removeChildFromNode(path, child+"/", appName, user, host);
}
}
// Key manipulation
void addKeyToNode(String path, String key, String value)
{
synchronized(MacOSXPreferencesFile.class) {
markChanged();
addKeyToNode(path, key, value, appName, user, host);
}
}
void removeKeyFromNode(String path, String key)
{
synchronized(MacOSXPreferencesFile.class) {
markChanged();
removeKeyFromNode(path, key, appName, user, host);
}
}
String getKeyFromNode(String path, String key)
{
synchronized(MacOSXPreferencesFile.class) {
return getKeyFromNode(path, key, appName, user, host);
}
}
// Enumerators
String[] getChildrenForNode(String path)
{
synchronized(MacOSXPreferencesFile.class) {
return getChildrenForNode(path, appName, user, host);
}
}
String[] getKeysForNode(String path)
{
synchronized(MacOSXPreferencesFile.class) {
return getKeysForNode(path, appName, user, host);
}
}
// Synchronization
boolean synchronize()
{
synchronized(MacOSXPreferencesFile.class) {
return synchronize(appName, user, host);
}
}
// CF functions
// Must be called when synchronized on MacOSXPreferencesFile.class
private static final native boolean
addNode(String path, String name, long user, long host);
private static final native void
removeNode(String path, String name, long user, long host);
private static final native boolean
addChildToNode(String path, String child,
String name, long user, long host);
private static final native void
removeChildFromNode(String path, String child,
String name, long user, long host);
private static final native void
addKeyToNode(String path, String key, String value,
String name, long user, long host);
private static final native void
removeKeyFromNode(String path, String key,
String name, long user, long host);
private static final native String
getKeyFromNode(String path, String key,
String name, long user, long host);
private static final native String[]
getChildrenForNode(String path, String name, long user, long host);
private static final native String[]
getKeysForNode(String path, String name, long user, long host);
private static final native boolean
synchronize(String name, long user, long host);
// CFPreferences host and user values (CFStringRefs)
private static long cfCurrentUser = currentUser();
private static long cfAnyUser = anyUser();
private static long cfCurrentHost = currentHost();
private static long cfAnyHost = anyHost();
// CFPreferences constant accessors
private static final native long currentUser();
private static final native long anyUser();
private static final native long currentHost();
private static final native long anyHost();
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
import java.io.NotSerializableException;
/**
* Thrown to indicate that a preferences operation could not complete because
* of a failure in the backing store, or a failure to contact the backing
* store.
*
* @author Josh Bloch
* @since 1.4
*/
public class BackingStoreException extends Exception {
/**
* Constructs a BackingStoreException with the specified detail message.
*
* @param s the detail message.
*/
public BackingStoreException(String s) {
super(s);
}
/**
* Constructs a BackingStoreException with the specified cause.
*
* @param cause the cause
*/
public BackingStoreException(Throwable cause) {
super(cause);
}
private static final long serialVersionUID = 859796500401108469L;
}

View file

@ -0,0 +1,261 @@
/*
* Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
/**
* Static methods for translating Base64 encoded strings to byte arrays
* and vice-versa.
*
* @author Josh Bloch
* @see Preferences
* @since 1.4
*/
class Base64 {
/**
* Translates the specified byte array into a Base64 string as per
* Preferences.put(byte[]).
*/
static String byteArrayToBase64(byte[] a) {
return byteArrayToBase64(a, false);
}
/**
* Translates the specified byte array into an "alternate representation"
* Base64 string. This non-standard variant uses an alphabet that does
* not contain the uppercase alphabetic characters, which makes it
* suitable for use in situations where case-folding occurs.
*/
static String byteArrayToAltBase64(byte[] a) {
return byteArrayToBase64(a, true);
}
private static String byteArrayToBase64(byte[] a, boolean alternate) {
int aLen = a.length;
int numFullGroups = aLen/3;
int numBytesInPartialGroup = aLen - 3*numFullGroups;
int resultLen = 4*((aLen + 2)/3);
StringBuilder result = new StringBuilder(resultLen);
char[] intToAlpha = (alternate ? intToAltBase64 : intToBase64);
// Translate all full groups from byte array elements to Base64
int inCursor = 0;
for (int i=0; i<numFullGroups; i++) {
int byte0 = a[inCursor++] & 0xff;
int byte1 = a[inCursor++] & 0xff;
int byte2 = a[inCursor++] & 0xff;
result.append(intToAlpha[byte0 >> 2]);
result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]);
result.append(intToAlpha[byte2 & 0x3f]);
}
// Translate partial group if present
if (numBytesInPartialGroup != 0) {
int byte0 = a[inCursor++] & 0xff;
result.append(intToAlpha[byte0 >> 2]);
if (numBytesInPartialGroup == 1) {
result.append(intToAlpha[(byte0 << 4) & 0x3f]);
result.append("==");
} else {
// assert numBytesInPartialGroup == 2;
int byte1 = a[inCursor++] & 0xff;
result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
result.append(intToAlpha[(byte1 << 2)&0x3f]);
result.append('=');
}
}
// assert inCursor == a.length;
// assert result.length() == resultLen;
return result.toString();
}
/**
* This array is a lookup table that translates 6-bit positive integer
* index values into their "Base64 Alphabet" equivalents as specified
* in Table 1 of RFC 2045.
*/
private static final char intToBase64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
/**
* This array is a lookup table that translates 6-bit positive integer
* index values into their "Alternate Base64 Alphabet" equivalents.
* This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045.
* This alternate alphabet does not use the capital letters. It is
* designed for use in environments where "case folding" occurs.
*/
private static final char intToAltBase64[] = {
'!', '"', '#', '$', '%', '&', '\'', '(', ')', ',', '-', '.', ':',
';', '<', '>', '@', '[', ']', '^', '`', '_', '{', '|', '}', '~',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '?'
};
/**
* Translates the specified Base64 string (as per Preferences.get(byte[]))
* into a byte array.
*
* @throws IllegalArgumentException if {@code s} is not a valid Base64
* string.
*/
static byte[] base64ToByteArray(String s) {
return base64ToByteArray(s, false);
}
/**
* Translates the specified "alternate representation" Base64 string
* into a byte array.
*
* @throws IllegalArgumentException or ArrayOutOfBoundsException
* if {@code s} is not a valid alternate representation
* Base64 string.
*/
static byte[] altBase64ToByteArray(String s) {
return base64ToByteArray(s, true);
}
private static byte[] base64ToByteArray(String s, boolean alternate) {
byte[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt);
int sLen = s.length();
int numGroups = sLen/4;
if (4*numGroups != sLen)
throw new IllegalArgumentException(
"String length must be a multiple of four.");
int missingBytesInLastGroup = 0;
int numFullGroups = numGroups;
if (sLen != 0) {
if (s.charAt(sLen-1) == '=') {
missingBytesInLastGroup++;
numFullGroups--;
}
if (s.charAt(sLen-2) == '=')
missingBytesInLastGroup++;
}
byte[] result = new byte[3*numGroups - missingBytesInLastGroup];
// Translate all full groups from base64 to byte array elements
int inCursor = 0, outCursor = 0;
for (int i=0; i<numFullGroups; i++) {
int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt);
int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt);
int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt);
int ch3 = base64toInt(s.charAt(inCursor++), alphaToInt);
result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
result[outCursor++] = (byte) ((ch2 << 6) | ch3);
}
// Translate partial group, if present
if (missingBytesInLastGroup != 0) {
int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt);
int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt);
result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
if (missingBytesInLastGroup == 1) {
int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt);
result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
}
}
// assert inCursor == s.length()-missingBytesInLastGroup;
// assert outCursor == result.length;
return result;
}
/**
* Translates the specified character, which is assumed to be in the
* "Base 64 Alphabet" into its equivalent 6-bit positive integer.
*
* @throws IllegalArgumentException or ArrayOutOfBoundsException if
* c is not in the Base64 Alphabet.
*/
private static int base64toInt(char c, byte[] alphaToInt) {
int result = alphaToInt[c];
if (result < 0)
throw new IllegalArgumentException("Illegal character " + c);
return result;
}
/**
* This array is a lookup table that translates unicode characters
* drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
* into their 6-bit positive integer equivalents. Characters that
* are not in the Base64 alphabet but fall within the bounds of the
* array are translated to -1.
*/
private static final byte base64ToInt[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
/**
* This array is the analogue of base64ToInt, but for the nonstandard
* variant that avoids the use of uppercase alphabetic characters.
*/
private static final byte altBase64ToInt[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1,
2, 3, 4, 5, 6, 7, 8, -1, 62, 9, 10, 11, -1 , 52, 53, 54, 55, 56, 57,
58, 59, 60, 61, 12, 13, 14, -1, 15, 63, 16, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 17, -1, 18, 19, 21, 20, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 22, 23, 24, 25
};
public static void main(String args[]) {
int numRuns = Integer.parseInt(args[0]);
int numBytes = Integer.parseInt(args[1]);
java.util.Random rnd = new java.util.Random();
for (int i=0; i<numRuns; i++) {
for (int j=0; j<numBytes; j++) {
byte[] arr = new byte[j];
for (int k=0; k<j; k++)
arr[k] = (byte)rnd.nextInt();
String s = byteArrayToBase64(arr);
byte [] b = base64ToByteArray(s);
if (!java.util.Arrays.equals(arr, b))
System.out.println("Dismal failure!");
s = byteArrayToAltBase64(arr);
b = altBase64ToByteArray(s);
if (!java.util.Arrays.equals(arr, b))
System.out.println("Alternate dismal failure!");
}
}
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
import java.io.NotSerializableException;
/**
* Thrown to indicate that an operation could not complete because
* the input did not conform to the appropriate XML document type
* for a collection of preferences, as per the {@link Preferences}
* specification.
*
* @author Josh Bloch
* @see Preferences
* @since 1.4
*/
public class InvalidPreferencesFormatException extends Exception {
/**
* Constructs an InvalidPreferencesFormatException with the specified
* cause.
*
* @param cause the cause (which is saved for later retrieval by the
* {@link Throwable#getCause()} method).
*/
public InvalidPreferencesFormatException(Throwable cause) {
super(cause);
}
/**
* Constructs an InvalidPreferencesFormatException with the specified
* detail message.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link Throwable#getMessage()} method.
*/
public InvalidPreferencesFormatException(String message) {
super(message);
}
/**
* Constructs an InvalidPreferencesFormatException with the specified
* detail message and cause.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link Throwable#getMessage()} method.
* @param cause the cause (which is saved for later retrieval by the
* {@link Throwable#getCause()} method).
*/
public InvalidPreferencesFormatException(String message, Throwable cause) {
super(message, cause);
}
private static final long serialVersionUID = -791715184232119669L;
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
import java.io.NotSerializableException;
/**
* An event emitted by a {@code Preferences} node to indicate that
* a child of that node has been added or removed.<p>
*
* Note, that although NodeChangeEvent inherits Serializable interface from
* java.util.EventObject, it is not intended to be Serializable. Appropriate
* serialization methods are implemented to throw NotSerializableException.
*
* @author Josh Bloch
* @see Preferences
* @see NodeChangeListener
* @see PreferenceChangeEvent
* @since 1.4
* @serial exclude
*/
public class NodeChangeEvent extends java.util.EventObject {
/**
* The node that was added or removed.
*/
private transient Preferences child;
/**
* Constructs a new {@code NodeChangeEvent} instance.
*
* @param parent The parent of the node that was added or removed.
* @param child The node that was added or removed.
*/
public NodeChangeEvent(Preferences parent, Preferences child) {
super(parent);
this.child = child;
}
/**
* Returns the parent of the node that was added or removed.
*
* @return The parent Preferences node whose child was added or removed
*/
public Preferences getParent() {
return (Preferences) getSource();
}
/**
* Returns the node that was added or removed.
*
* @return The node that was added or removed.
*/
public Preferences getChild() {
return child;
}
/**
* Throws NotSerializableException, since NodeChangeEvent objects are not
* intended to be serializable.
*/
private void writeObject(java.io.ObjectOutputStream out)
throws NotSerializableException {
throw new NotSerializableException("Not serializable.");
}
/**
* Throws NotSerializableException, since NodeChangeEvent objects are not
* intended to be serializable.
*/
private void readObject(java.io.ObjectInputStream in)
throws NotSerializableException {
throw new NotSerializableException("Not serializable.");
}
// Defined so that this class isn't flagged as a potential problem when
// searches for missing serialVersionUID fields are done.
private static final long serialVersionUID = 8068949086596572957L;
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
/**
* A listener for receiving preference node change events.
*
* @author Josh Bloch
* @see Preferences
* @see NodeChangeEvent
* @see PreferenceChangeListener
* @since 1.4
*/
public interface NodeChangeListener extends java.util.EventListener {
/**
* This method gets called when a child node is added.
*
* @param evt A node change event object describing the parent
* and child node.
*/
void childAdded(NodeChangeEvent evt);
/**
* This method gets called when a child node is removed.
*
* @param evt A node change event object describing the parent
* and child node.
*/
void childRemoved(NodeChangeEvent evt);
}

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
import java.io.NotSerializableException;
/**
* An event emitted by a {@code Preferences} node to indicate that
* a preference has been added, removed or has had its value changed.<p>
*
* Note, that although PreferenceChangeEvent inherits Serializable interface
* from EventObject, it is not intended to be Serializable. Appropriate
* serialization methods are implemented to throw NotSerializableException.
*
* @author Josh Bloch
* @see Preferences
* @see PreferenceChangeListener
* @see NodeChangeEvent
* @since 1.4
* @serial exclude
*/
public class PreferenceChangeEvent extends java.util.EventObject {
/**
* Key of the preference that changed.
*
* @serial
*/
private String key;
/**
* New value for preference, or {@code null} if it was removed.
*
* @serial
*/
private String newValue;
/**
* Constructs a new {@code PreferenceChangeEvent} instance.
*
* @param node The Preferences node that emitted the event.
* @param key The key of the preference that was changed.
* @param newValue The new value of the preference, or {@code null}
* if the preference is being removed.
*/
public PreferenceChangeEvent(Preferences node, String key,
String newValue) {
super(node);
this.key = key;
this.newValue = newValue;
}
/**
* Returns the preference node that emitted the event.
*
* @return The preference node that emitted the event.
*/
public Preferences getNode() {
return (Preferences) getSource();
}
/**
* Returns the key of the preference that was changed.
*
* @return The key of the preference that was changed.
*/
public String getKey() {
return key;
}
/**
* Returns the new value for the preference.
*
* @return The new value for the preference, or {@code null} if the
* preference was removed.
*/
public String getNewValue() {
return newValue;
}
/**
* Throws NotSerializableException, since NodeChangeEvent objects
* are not intended to be serializable.
*/
private void writeObject(java.io.ObjectOutputStream out)
throws NotSerializableException {
throw new NotSerializableException("Not serializable.");
}
/**
* Throws NotSerializableException, since PreferenceChangeEvent objects
* are not intended to be serializable.
*/
private void readObject(java.io.ObjectInputStream in)
throws NotSerializableException {
throw new NotSerializableException("Not serializable.");
}
// Defined so that this class isn't flagged as a potential problem when
// searches for missing serialVersionUID fields are done.
private static final long serialVersionUID = 793724513368024975L;
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
/**
* A listener for receiving preference change events.
*
* @author Josh Bloch
* @see Preferences
* @see PreferenceChangeEvent
* @see NodeChangeListener
* @since 1.4
*/
@FunctionalInterface
public interface PreferenceChangeListener extends java.util.EventListener {
/**
* This method gets called when a preference is added, removed or when
* its value is changed.
*
* @param evt A PreferenceChangeEvent object describing the event source
* and the preference that has changed.
*/
void preferenceChange(PreferenceChangeEvent evt);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
import java.util.*;
/**
* A factory object that generates Preferences objects. Providers of
* new {@link Preferences} implementations should provide corresponding
* {@code PreferencesFactory} implementations so that the new
* {@code Preferences} implementation can be installed in place of the
* platform-specific default implementation.
*
* <p><strong>This class is for {@code Preferences} implementers only.
* Normal users of the {@code Preferences} facility should have no need to
* consult this documentation.</strong>
*
* @author Josh Bloch
* @see Preferences
* @since 1.4
*/
public interface PreferencesFactory {
/**
* Returns the system root preference node. (Multiple calls on this
* method will return the same object reference.)
* @return the system root preference node
*/
Preferences systemRoot();
/**
* Returns the user root preference node corresponding to the calling
* user. In a server, the returned value will typically depend on
* some implicit client-context.
* @return the user root preference node corresponding to the calling
* user
*/
Preferences userRoot();
}

View file

@ -0,0 +1,422 @@
/*
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
import java.util.*;
import java.io.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.xml.sax.*;
import org.w3c.dom.*;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* XML Support for java.util.prefs. Methods to import and export preference
* nodes and subtrees.
*
* @author Josh Bloch and Mark Reinhold
* @see Preferences
* @since 1.4
*/
class XmlSupport {
// The required DTD URI for exported preferences
private static final String PREFS_DTD_URI =
"http://java.sun.com/dtd/preferences.dtd";
// The actual DTD corresponding to the URI
private static final String PREFS_DTD =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<!-- DTD for preferences -->" +
"<!ELEMENT preferences (root) >" +
"<!ATTLIST preferences" +
" EXTERNAL_XML_VERSION CDATA \"0.0\" >" +
"<!ELEMENT root (map, node*) >" +
"<!ATTLIST root" +
" type (system|user) #REQUIRED >" +
"<!ELEMENT node (map, node*) >" +
"<!ATTLIST node" +
" name CDATA #REQUIRED >" +
"<!ELEMENT map (entry*) >" +
"<!ATTLIST map" +
" MAP_XML_VERSION CDATA \"0.0\" >" +
"<!ELEMENT entry EMPTY >" +
"<!ATTLIST entry" +
" key CDATA #REQUIRED" +
" value CDATA #REQUIRED >" ;
/**
* Version number for the format exported preferences files.
*/
private static final String EXTERNAL_XML_VERSION = "1.0";
/*
* Version number for the internal map files.
*/
private static final String MAP_XML_VERSION = "1.0";
/**
* Export the specified preferences node and, if subTree is true, all
* subnodes, to the specified output stream. Preferences are exported as
* an XML document conforming to the definition in the Preferences spec.
*
* @throws IOException if writing to the specified output stream
* results in an {@code IOException}.
* @throws BackingStoreException if preference data cannot be read from
* backing store.
* @throws IllegalStateException if this node (or an ancestor) has been
* removed with the {@link Preferences#removeNode()} method.
*/
static void export(OutputStream os, final Preferences p, boolean subTree)
throws IOException, BackingStoreException {
if (((AbstractPreferences)p).isRemoved())
throw new IllegalStateException("Node has been removed");
Document doc = createPrefsDoc("preferences");
Element preferences = doc.getDocumentElement() ;
preferences.setAttribute("EXTERNAL_XML_VERSION", EXTERNAL_XML_VERSION);
Element xmlRoot = (Element)
preferences.appendChild(doc.createElement("root"));
xmlRoot.setAttribute("type", (p.isUserNode() ? "user" : "system"));
// Get bottom-up list of nodes from p to root, excluding root
List<Preferences> ancestors = new ArrayList<>();
for (Preferences kid = p, dad = kid.parent(); dad != null;
kid = dad, dad = kid.parent()) {
ancestors.add(kid);
}
Element e = xmlRoot;
for (int i=ancestors.size()-1; i >= 0; i--) {
e.appendChild(doc.createElement("map"));
e = (Element) e.appendChild(doc.createElement("node"));
e.setAttribute("name", ancestors.get(i).name());
}
putPreferencesInXml(e, doc, p, subTree);
writeDoc(doc, os);
}
/**
* Put the preferences in the specified Preferences node into the
* specified XML element which is assumed to represent a node
* in the specified XML document which is assumed to conform to
* PREFS_DTD. If subTree is true, create children of the specified
* XML node conforming to all of the children of the specified
* Preferences node and recurse.
*
* @throws BackingStoreException if it is not possible to read
* the preferences or children out of the specified
* preferences node.
*/
private static void putPreferencesInXml(Element elt, Document doc,
Preferences prefs, boolean subTree) throws BackingStoreException
{
Preferences[] kidsCopy = null;
String[] kidNames = null;
// Node is locked to export its contents and get a
// copy of children, then lock is released,
// and, if subTree = true, recursive calls are made on children
synchronized (((AbstractPreferences)prefs).lock) {
// Check if this node was concurrently removed. If yes
// remove it from XML Document and return.
if (((AbstractPreferences)prefs).isRemoved()) {
elt.getParentNode().removeChild(elt);
return;
}
// Put map in xml element
String[] keys = prefs.keys();
Element map = (Element) elt.appendChild(doc.createElement("map"));
for (String key : keys) {
Element entry = (Element)
map.appendChild(doc.createElement("entry"));
entry.setAttribute("key", key);
// NEXT STATEMENT THROWS NULL PTR EXC INSTEAD OF ASSERT FAIL
entry.setAttribute("value", prefs.get(key, null));
}
// Recurse if appropriate
if (subTree) {
/* Get a copy of kids while lock is held */
kidNames = prefs.childrenNames();
kidsCopy = new Preferences[kidNames.length];
for (int i = 0; i < kidNames.length; i++)
kidsCopy[i] = prefs.node(kidNames[i]);
}
// release lock
}
if (subTree) {
for (int i=0; i < kidNames.length; i++) {
Element xmlKid = (Element)
elt.appendChild(doc.createElement("node"));
xmlKid.setAttribute("name", kidNames[i]);
putPreferencesInXml(xmlKid, doc, kidsCopy[i], subTree);
}
}
}
/**
* Import preferences from the specified input stream, which is assumed
* to contain an XML document in the format described in the Preferences
* spec.
*
* @throws IOException if reading from the specified output stream
* results in an {@code IOException}.
* @throws InvalidPreferencesFormatException Data on input stream does not
* constitute a valid XML document with the mandated document type.
*/
static void importPreferences(InputStream is)
throws IOException, InvalidPreferencesFormatException
{
try {
Document doc = loadPrefsDoc(is);
String xmlVersion =
doc.getDocumentElement().getAttribute("EXTERNAL_XML_VERSION");
if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0)
throw new InvalidPreferencesFormatException(
"Exported preferences file format version " + xmlVersion +
" is not supported. This java installation can read" +
" versions " + EXTERNAL_XML_VERSION + " or older. You may need" +
" to install a newer version of JDK.");
Element xmlRoot = (Element) doc.getDocumentElement().
getChildNodes().item(0);
Preferences prefsRoot =
(xmlRoot.getAttribute("type").equals("user") ?
Preferences.userRoot() : Preferences.systemRoot());
ImportSubtree(prefsRoot, xmlRoot);
} catch(SAXException e) {
throw new InvalidPreferencesFormatException(e);
}
}
/**
* Create a new prefs XML document.
*/
private static Document createPrefsDoc( String qname ) {
try {
DOMImplementation di = DocumentBuilderFactory.newInstance().
newDocumentBuilder().getDOMImplementation();
DocumentType dt = di.createDocumentType(qname, null, PREFS_DTD_URI);
return di.createDocument(null, qname, dt);
} catch(ParserConfigurationException e) {
throw new AssertionError(e);
}
}
/**
* Load an XML document from specified input stream, which must
* have the requisite DTD URI.
*/
private static Document loadPrefsDoc(InputStream in)
throws SAXException, IOException
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setIgnoringElementContentWhitespace(true);
dbf.setValidating(true);
dbf.setCoalescing(true);
dbf.setIgnoringComments(true);
try {
DocumentBuilder db = dbf.newDocumentBuilder();
db.setEntityResolver(new Resolver());
db.setErrorHandler(new EH());
return db.parse(new InputSource(in));
} catch (ParserConfigurationException e) {
throw new AssertionError(e);
}
}
/**
* Write XML document to the specified output stream.
*/
private static final void writeDoc(Document doc, OutputStream out)
throws IOException
{
try {
TransformerFactory tf = TransformerFactory.newInstance();
try {
tf.setAttribute("indent-number", 2);
} catch (IllegalArgumentException iae) {
//Ignore the IAE. Should not fail the writeout even the
//transformer provider does not support "indent-number".
}
Transformer t = tf.newTransformer();
t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId());
t.setOutputProperty(OutputKeys.INDENT, "yes");
//Transformer resets the "indent" info if the "result" is a StreamResult with
//an OutputStream object embedded, creating a Writer object on top of that
//OutputStream object however works.
t.transform(new DOMSource(doc),
new StreamResult(new BufferedWriter(new OutputStreamWriter(out, UTF_8))));
} catch(TransformerException e) {
throw new AssertionError(e);
}
}
/**
* Recursively traverse the specified preferences node and store
* the described preferences into the system or current user
* preferences tree, as appropriate.
*/
private static void ImportSubtree(Preferences prefsNode, Element xmlNode) {
NodeList xmlKids = xmlNode.getChildNodes();
int numXmlKids = xmlKids.getLength();
/*
* We first lock the node, import its contents and get
* child nodes. Then we unlock the node and go to children
* Since some of the children might have been concurrently
* deleted we check for this.
*/
Preferences[] prefsKids;
/* Lock the node */
synchronized (((AbstractPreferences)prefsNode).lock) {
//If removed, return silently
if (((AbstractPreferences)prefsNode).isRemoved())
return;
// Import any preferences at this node
Element firstXmlKid = (Element) xmlKids.item(0);
ImportPrefs(prefsNode, firstXmlKid);
prefsKids = new Preferences[numXmlKids - 1];
// Get involved children
for (int i=1; i < numXmlKids; i++) {
Element xmlKid = (Element) xmlKids.item(i);
prefsKids[i-1] = prefsNode.node(xmlKid.getAttribute("name"));
}
} // unlocked the node
// import children
for (int i=1; i < numXmlKids; i++)
ImportSubtree(prefsKids[i-1], (Element)xmlKids.item(i));
}
/**
* Import the preferences described by the specified XML element
* (a map from a preferences document) into the specified
* preferences node.
*/
private static void ImportPrefs(Preferences prefsNode, Element map) {
NodeList entries = map.getChildNodes();
for (int i=0, numEntries = entries.getLength(); i < numEntries; i++) {
Element entry = (Element) entries.item(i);
prefsNode.put(entry.getAttribute("key"),
entry.getAttribute("value"));
}
}
/**
* Export the specified Map<String,String> to a map document on
* the specified OutputStream as per the prefs DTD. This is used
* as the internal (undocumented) format for FileSystemPrefs.
*
* @throws IOException if writing to the specified output stream
* results in an {@code IOException}.
*/
static void exportMap(OutputStream os, Map<String, String> map) throws IOException {
Document doc = createPrefsDoc("map");
Element xmlMap = doc.getDocumentElement( ) ;
xmlMap.setAttribute("MAP_XML_VERSION", MAP_XML_VERSION);
for (Map.Entry<String, String> e : map.entrySet()) {
Element xe = (Element)
xmlMap.appendChild(doc.createElement("entry"));
xe.setAttribute("key", e.getKey());
xe.setAttribute("value", e.getValue());
}
writeDoc(doc, os);
}
/**
* Import Map from the specified input stream, which is assumed
* to contain a map document as per the prefs DTD. This is used
* as the internal (undocumented) format for FileSystemPrefs. The
* key-value pairs specified in the XML document will be put into
* the specified Map. (If this Map is empty, it will contain exactly
* the key-value pairs int the XML-document when this method returns.)
*
* @throws IOException if reading from the specified output stream
* results in an {@code IOException}.
* @throws InvalidPreferencesFormatException Data on input stream does not
* constitute a valid XML document with the mandated document type.
*/
static void importMap(InputStream is, Map<String, String> m)
throws IOException, InvalidPreferencesFormatException
{
try {
Document doc = loadPrefsDoc(is);
Element xmlMap = doc.getDocumentElement();
// check version
String mapVersion = xmlMap.getAttribute("MAP_XML_VERSION");
if (mapVersion.compareTo(MAP_XML_VERSION) > 0)
throw new InvalidPreferencesFormatException(
"Preferences map file format version " + mapVersion +
" is not supported. This java installation can read" +
" versions " + MAP_XML_VERSION + " or older. You may need" +
" to install a newer version of JDK.");
NodeList entries = xmlMap.getChildNodes();
for (int i=0, numEntries=entries.getLength(); i<numEntries; i++) {
Element entry = (Element) entries.item(i);
m.put(entry.getAttribute("key"), entry.getAttribute("value"));
}
} catch(SAXException e) {
throw new InvalidPreferencesFormatException(e);
}
}
private static class Resolver implements EntityResolver {
public InputSource resolveEntity(String pid, String sid)
throws SAXException
{
if (sid.equals(PREFS_DTD_URI)) {
InputSource is;
is = new InputSource(new StringReader(PREFS_DTD));
is.setSystemId(PREFS_DTD_URI);
return is;
}
throw new SAXException("Invalid system identifier: " + sid);
}
}
private static class EH implements ErrorHandler {
public void error(SAXParseException x) throws SAXException {
throw x;
}
public void fatalError(SAXParseException x) throws SAXException {
throw x;
}
public void warning(SAXParseException x) throws SAXException {
throw x;
}
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* This package allows applications to store and retrieve user and system
* preference and configuration data. This data is stored persistently in an
* implementation-dependent backing store. There are two separate trees of
* preference nodes, one for user preferences and one for system preferences.
*
* @since 1.4
*/
package java.util.prefs;

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Defines the Preferences API.
*
* @uses java.util.prefs.PreferencesFactory
*
* @moduleGraph
* @since 9
*/
module java.prefs {
requires java.xml;
exports java.util.prefs;
uses java.util.prefs.PreferencesFactory;
}

View file

@ -0,0 +1,954 @@
/*
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
import java.util.*;
import java.io.*;
import java.security.PrivilegedActionException;
import sun.util.logging.PlatformLogger;
/**
* Preferences implementation for Unix. Preferences are stored in the file
* system, with one directory per preferences node. All of the preferences
* at each node are stored in a single file. Atomic file system operations
* (e.g. File.renameTo) are used to ensure integrity. An in-memory cache of
* the "explored" portion of the tree is maintained for performance, and
* written back to the disk periodically. File-locking is used to ensure
* reasonable behavior when multiple VMs are running at the same time.
* (The file lock is obtained only for sync(), flush() and removeNode().)
*
* @author Josh Bloch
* @see Preferences
* @since 1.4
*/
class FileSystemPreferences extends AbstractPreferences {
static {
loadPrefsLib();
}
@SuppressWarnings("restricted")
private static void loadPrefsLib() {
System.loadLibrary("prefs");
}
/**
* Sync interval in seconds.
*/
private static final int SYNC_INTERVAL = Math.max(1,
Integer.getInteger("java.util.prefs.syncInterval", 30));
/**
* Returns logger for error messages. Backing store exceptions are logged at
* WARNING level.
*/
private static PlatformLogger getLogger() {
return PlatformLogger.getLogger("java.util.prefs");
}
/**
* Directory for system preferences.
*/
private static File systemRootDir;
/*
* Flag, indicating whether systemRoot directory is writable
*/
private static boolean isSystemRootWritable;
/**
* Directory for user preferences.
*/
private static File userRootDir;
/*
* Flag, indicating whether userRoot directory is writable
*/
private static boolean isUserRootWritable;
/**
* The user root.
*/
private static volatile Preferences userRoot;
static Preferences getUserRoot() {
Preferences root = userRoot;
if (root == null) {
synchronized (FileSystemPreferences.class) {
root = userRoot;
if (root == null) {
setupUserRoot();
userRoot = root = new FileSystemPreferences(true);
}
}
}
return root;
}
private static void setupUserRoot() {
userRootDir =
new File(System.getProperty("java.util.prefs.userRoot",
System.getProperty("user.home")), ".java/.userPrefs");
// Attempt to create root dir if it does not yet exist.
if (!userRootDir.exists()) {
if (userRootDir.mkdirs()) {
try {
chmod(userRootDir.getCanonicalPath(), USER_RWX);
} catch (IOException e) {
getLogger().warning("Could not change permissions" +
" on userRoot directory. ");
}
getLogger().info("Created user preferences directory.");
} else {
getLogger().warning("Couldn't create user preferences" +
" directory. User preferences are unusable.");
}
}
isUserRootWritable = userRootDir.canWrite();
String USER_NAME = System.getProperty("user.name");
userLockFile = new File(userRootDir,".user.lock." + USER_NAME);
userRootModFile = new File (userRootDir,
".userRootModFile." + USER_NAME);
if (!userRootModFile.exists()) {
try {
// create if does not exist.
userRootModFile.createNewFile();
// Only user can read/write userRootModFile.
int result = chmod(userRootModFile.getCanonicalPath(),
USER_READ_WRITE);
if (result != 0)
getLogger().warning("Problem creating userRoot " +
"mod file. Chmod failed on " +
userRootModFile.getCanonicalPath() +
" Unix error code " + result);
} catch (IOException e) {
getLogger().warning(e.toString());
}
}
userRootModTime = userRootModFile.lastModified();
}
/**
* The system root.
*/
private static volatile Preferences systemRoot;
static Preferences getSystemRoot() {
Preferences root = systemRoot;
if (root == null) {
synchronized (FileSystemPreferences.class) {
root = systemRoot;
if (root == null) {
setupSystemRoot();
systemRoot = root = new FileSystemPreferences(false);
}
}
}
return root;
}
private static void setupSystemRoot() {
String systemPrefsDirName =
System.getProperty("java.util.prefs.systemRoot", "/etc/.java");
systemRootDir =
new File(systemPrefsDirName, ".systemPrefs");
// Attempt to create root dir if it does not yet exist.
if (!systemRootDir.exists()) {
// system root does not exist in /etc/.java
// Switching to java.home
systemRootDir =
new File(System.getProperty("java.home"),
".systemPrefs");
if (!systemRootDir.exists()) {
if (systemRootDir.mkdirs()) {
getLogger().info(
"Created system preferences directory "
+ "in java.home.");
try {
chmod(systemRootDir.getCanonicalPath(),
USER_RWX_ALL_RX);
} catch (IOException e) {
}
} else {
getLogger().warning("Could not create "
+ "system preferences directory. System "
+ "preferences are unusable.");
}
}
}
isSystemRootWritable = systemRootDir.canWrite();
systemLockFile = new File(systemRootDir, ".system.lock");
systemRootModFile = new File (systemRootDir, ".systemRootModFile");
if (!systemRootModFile.exists() && isSystemRootWritable) {
try {
// create if does not exist.
systemRootModFile.createNewFile();
int result = chmod(systemRootModFile.getCanonicalPath(),
USER_RW_ALL_READ);
if (result != 0) {
getLogger().warning("Chmod failed on " +
systemRootModFile.getCanonicalPath() +
" Unix error code " + result);
}
} catch (IOException e) {
getLogger().warning(e.toString());
}
}
systemRootModTime = systemRootModFile.lastModified();
}
/**
* Unix user write/read permission
*/
private static final int USER_READ_WRITE = 0600;
private static final int USER_RW_ALL_READ = 0644;
private static final int USER_RWX_ALL_RX = 0755;
private static final int USER_RWX = 0700;
/**
* The lock file for the user tree.
*/
static File userLockFile;
/**
* The lock file for the system tree.
*/
static File systemLockFile;
/**
* Unix lock handle for userRoot.
* Zero, if unlocked.
*/
private static int userRootLockHandle = 0;
/**
* Unix lock handle for systemRoot.
* Zero, if unlocked.
*/
private static int systemRootLockHandle = 0;
/**
* The directory representing this preference node. There is no guarantee
* that this directory exits, as another VM can delete it at any time
* that it (the other VM) holds the file-lock. While the root node cannot
* be deleted, it may not yet have been created, or the underlying
* directory could have been deleted accidentally.
*/
private final File dir;
/**
* The file representing this preference node's preferences.
* The file format is undocumented, and subject to change
* from release to release, but I'm sure that you can figure
* it out if you try real hard.
*/
private final File prefsFile;
/**
* A temporary file used for saving changes to preferences. As part of
* the sync operation, changes are first saved into this file, and then
* atomically renamed to prefsFile. This results in an atomic state
* change from one valid set of preferences to another.
* The file-lock is held for the duration of this transformation.
*/
private final File tmpFile;
/**
* File, which keeps track of global modifications of userRoot.
*/
private static File userRootModFile;
/**
* Flag, which indicated whether userRoot was modified by another VM
*/
private static boolean isUserRootModified = false;
/**
* Keeps track of userRoot modification time. This time is reset to
* zero after UNIX reboot, and is increased by 1 second each time
* userRoot is modified.
*/
private static long userRootModTime;
/*
* File, which keeps track of global modifications of systemRoot
*/
private static File systemRootModFile;
/*
* Flag, which indicates whether systemRoot was modified by another VM
*/
private static boolean isSystemRootModified = false;
/**
* Keeps track of systemRoot modification time. This time is reset to
* zero after system reboot, and is increased by 1 second each time
* systemRoot is modified.
*/
private static long systemRootModTime;
/**
* Locally cached preferences for this node (includes uncommitted
* changes). This map is initialized with from disk when the first get or
* put operation occurs on this node. It is synchronized with the
* corresponding disk file (prefsFile) by the sync operation. The initial
* value is read *without* acquiring the file-lock.
*/
private Map<String, String> prefsCache = null;
/**
* The last modification time of the file backing this node at the time
* that prefCache was last synchronized (or initially read). This
* value is set *before* reading the file, so it's conservative; the
* actual timestamp could be (slightly) higher. A value of zero indicates
* that we were unable to initialize prefsCache from the disk, or
* have not yet attempted to do so. (If prefsCache is non-null, it
* indicates the former; if it's null, the latter.)
*/
private long lastSyncTime = 0;
/**
* Unix error code for locked file.
*/
private static final int EAGAIN = 11;
/**
* Unix error code for denied access.
*/
private static final int EACCES = 13;
/* Used to interpret results of native functions */
private static final int LOCK_HANDLE = 0;
private static final int ERROR_CODE = 1;
/**
* A list of all uncommitted preference changes. The elements in this
* list are of type PrefChange. If this node is concurrently modified on
* disk by another VM, the two sets of changes are merged when this node
* is sync'ed by overwriting our prefsCache with the preference map last
* written out to disk (by the other VM), and then replaying this change
* log against that map. The resulting map is then written back
* to the disk.
*/
final List<Change> changeLog = new ArrayList<>();
/**
* Represents a change to a preference.
*/
private abstract static class Change {
/**
* Reapplies the change to prefsCache.
*/
abstract void replay();
}
/**
* Represents a preference put.
*/
private class Put extends Change {
String key, value;
Put(String key, String value) {
this.key = key;
this.value = value;
}
void replay() {
prefsCache.put(key, value);
}
}
/**
* Represents a preference remove.
*/
private class Remove extends Change {
String key;
Remove(String key) {
this.key = key;
}
void replay() {
prefsCache.remove(key);
}
}
/**
* Represents the creation of this node.
*/
private static class NodeCreate extends Change {
/**
* Performs no action, but the presence of this object in changeLog
* will force the node and its ancestors to be made permanent at the
* next sync.
*/
void replay() {
}
}
/**
* NodeCreate object for this node.
*/
NodeCreate nodeCreate = null;
/**
* Replay changeLog against prefsCache.
*/
private void replayChanges() {
for (int i = 0, n = changeLog.size(); i<n; i++)
changeLog.get(i).replay();
}
private static final Timer syncTimer = new Timer(true); // Daemon Thread
static {
addShutdownHook();
}
private static void addShutdownHook() {
// Add periodic timer task to periodically sync cached prefs
syncTimer.schedule(new TimerTask() {
public void run() {
syncWorld();
}
}, SYNC_INTERVAL*1000, SYNC_INTERVAL*1000);
// Add shutdown hook to flush cached prefs on normal termination
Runtime.getRuntime().addShutdownHook(
new Thread(null, null, "Sync Timer Thread", 0, false) {
public void run() {
syncTimer.cancel();
syncWorld();
}
});
}
private static void syncWorld() {
/*
* Synchronization necessary because userRoot and systemRoot are
* lazily initialized.
*/
Preferences userRt;
Preferences systemRt;
synchronized(FileSystemPreferences.class) {
userRt = userRoot;
systemRt = systemRoot;
}
try {
if (userRt != null)
userRt.flush();
} catch(BackingStoreException e) {
getLogger().warning("Couldn't flush user prefs: " + e);
}
try {
if (systemRt != null)
systemRt.flush();
} catch(BackingStoreException e) {
getLogger().warning("Couldn't flush system prefs: " + e);
}
}
private final boolean isUserNode;
/**
* Special constructor for roots (both user and system). This constructor
* will only be called twice, by the static initializer.
*/
private FileSystemPreferences(boolean user) {
super(null, "");
isUserNode = user;
dir = (user ? userRootDir: systemRootDir);
prefsFile = new File(dir, "prefs.xml");
tmpFile = new File(dir, "prefs.tmp");
}
/**
* Construct a new FileSystemPreferences instance with the specified
* parent node and name. This constructor, called from childSpi,
* is used to make every node except for the two //roots.
*/
private FileSystemPreferences(FileSystemPreferences parent, String name) {
super(parent, name);
isUserNode = parent.isUserNode;
dir = new File(parent.dir, dirName(name));
prefsFile = new File(dir, "prefs.xml");
tmpFile = new File(dir, "prefs.tmp");
newNode = !dir.exists();
if (newNode) {
// These 2 things guarantee node will get written at next flush/sync
prefsCache = new TreeMap<>();
nodeCreate = new NodeCreate();
changeLog.add(nodeCreate);
}
}
public boolean isUserNode() {
return isUserNode;
}
protected void putSpi(String key, String value) {
initCacheIfNecessary();
changeLog.add(new Put(key, value));
prefsCache.put(key, value);
}
protected String getSpi(String key) {
initCacheIfNecessary();
return prefsCache.get(key);
}
protected void removeSpi(String key) {
initCacheIfNecessary();
changeLog.add(new Remove(key));
prefsCache.remove(key);
}
/**
* Initialize prefsCache if it has yet to be initialized. When this method
* returns, prefsCache will be non-null. If the data was successfully
* read from the file, lastSyncTime will be updated. If prefsCache was
* null, but it was impossible to read the file (because it didn't
* exist or for any other reason) prefsCache will be initialized to an
* empty, modifiable Map, and lastSyncTime remain zero.
*/
private void initCacheIfNecessary() {
if (prefsCache != null)
return;
try {
loadCache();
} catch(Exception e) {
// assert lastSyncTime == 0;
prefsCache = new TreeMap<>();
}
}
/**
* Attempt to load prefsCache from the backing store. If the attempt
* succeeds, lastSyncTime will be updated (the new value will typically
* correspond to the data loaded into the map, but it may be less,
* if another VM is updating this node concurrently). If the attempt
* fails, a BackingStoreException is thrown and both prefsCache and
* lastSyncTime are unaffected by the call.
*/
private void loadCache() throws BackingStoreException {
Map<String, String> m = new TreeMap<>();
long newLastSyncTime = 0;
try {
newLastSyncTime = prefsFile.lastModified();
try (FileInputStream fis = new FileInputStream(prefsFile)) {
XmlSupport.importMap(fis, m);
}
} catch(Exception e) {
if (e instanceof InvalidPreferencesFormatException) {
getLogger().warning("Invalid preferences format in "
+ prefsFile.getPath());
prefsFile.renameTo( new File(
prefsFile.getParentFile(),
"IncorrectFormatPrefs.xml"));
m = new TreeMap<>();
} else if (e instanceof FileNotFoundException) {
getLogger().warning("Prefs file removed in background "
+ prefsFile.getPath());
} else {
throw new BackingStoreException(e);
}
}
// Attempt succeeded; update state
prefsCache = m;
lastSyncTime = newLastSyncTime;
}
/**
* Attempt to write back prefsCache to the backing store. If the attempt
* succeeds, lastSyncTime will be updated (the new value will correspond
* exactly to the data thust written back, as we hold the file lock, which
* prevents a concurrent write. If the attempt fails, a
* BackingStoreException is thrown and both the backing store (prefsFile)
* and lastSyncTime will be unaffected by this call. This call will
* NEVER leave prefsFile in a corrupt state.
*/
private void writeBackCache() throws BackingStoreException {
try {
if (!dir.exists() && !dir.mkdirs())
throw new BackingStoreException(dir +
" create failed.");
try (FileOutputStream fos = new FileOutputStream(tmpFile)) {
XmlSupport.exportMap(fos, prefsCache);
}
if (!tmpFile.renameTo(prefsFile))
throw new BackingStoreException("Can't rename " +
tmpFile + " to " + prefsFile);
} catch(BackingStoreException e) {
throw e;
} catch(Exception e) {
throw new BackingStoreException(e);
}
}
protected String[] keysSpi() {
initCacheIfNecessary();
return prefsCache.keySet().toArray(new String[prefsCache.size()]);
}
protected String[] childrenNamesSpi() {
List<String> result = new ArrayList<>();
File[] dirContents = dir.listFiles();
if (dirContents != null) {
for (int i = 0; i < dirContents.length; i++)
if (dirContents[i].isDirectory())
result.add(nodeName(dirContents[i].getName()));
}
return result.toArray(EMPTY_STRING_ARRAY);
}
private static final String[] EMPTY_STRING_ARRAY = new String[0];
protected AbstractPreferences childSpi(String name) {
return new FileSystemPreferences(this, name);
}
public void removeNode() throws BackingStoreException {
synchronized (isUserNode()? userLockFile: systemLockFile) {
// to remove a node we need an exclusive lock
if (!lockFile(false))
throw(new BackingStoreException("Couldn't get file lock."));
try {
super.removeNode();
} finally {
unlockFile();
}
}
}
/**
* Called with file lock held (in addition to node locks).
*/
protected void removeNodeSpi() throws BackingStoreException {
if (changeLog.contains(nodeCreate)) {
changeLog.remove(nodeCreate);
nodeCreate = null;
return;
}
if (!dir.exists())
return;
prefsFile.delete();
tmpFile.delete();
// dir should be empty now. If it's not, empty it
File[] junk = dir.listFiles();
if (junk.length != 0) {
getLogger().warning(
"Found extraneous files when removing node: "
+ Arrays.asList(junk));
for (int i=0; i<junk.length; i++)
junk[i].delete();
}
if (!dir.delete())
throw new BackingStoreException("Couldn't delete dir: "
+ dir);
}
public synchronized void sync() throws BackingStoreException {
boolean userNode = isUserNode();
boolean shared;
if (userNode) {
shared = false; /* use exclusive lock for user prefs */
} else {
/* if can write to system root, use exclusive lock.
otherwise use shared lock. */
shared = !isSystemRootWritable;
}
synchronized (isUserNode()? userLockFile:systemLockFile) {
if (!lockFile(shared)) {
throw (new BackingStoreException("Couldn't get file lock."));
}
long nmt;
if (isUserNode()) {
nmt = userRootModFile.lastModified();
isUserRootModified = userRootModTime == nmt;
} else {
nmt = systemRootModFile.lastModified();
isSystemRootModified = systemRootModTime == nmt;
}
final long newModTime = nmt;
try {
super.sync();
if (isUserNode()) {
userRootModTime = newModTime + 1000;
userRootModFile.setLastModified(userRootModTime);
} else {
systemRootModTime = newModTime + 1000;
systemRootModFile.setLastModified(systemRootModTime);
}
} finally {
unlockFile();
}
}
}
protected void syncSpi() throws BackingStoreException {
if (isRemoved())
throw new IllegalStateException("Node has been removed");
if (prefsCache == null)
return; // We've never been used, don't bother syncing
long lastModifiedTime;
if ((isUserNode() ? isUserRootModified : isSystemRootModified)) {
lastModifiedTime = prefsFile.lastModified();
if (lastModifiedTime != lastSyncTime) {
// Prefs at this node were externally modified; read in node and
// playback any local mods since last sync
loadCache();
replayChanges();
lastSyncTime = lastModifiedTime;
}
} else if (lastSyncTime != 0 && !dir.exists()) {
// This node was removed in the background. Playback any changes
// against a virgin (empty) Map.
prefsCache = new TreeMap<>();
replayChanges();
}
if (!changeLog.isEmpty()) {
writeBackCache(); // Creates directory & file if necessary
/*
* Attempt succeeded; it's barely possible that the call to
* lastModified might fail (i.e., return 0), but this would not
* be a disaster, as lastSyncTime is allowed to lag.
*/
lastModifiedTime = prefsFile.lastModified();
/* If lastSyncTime did not change, or went back
* increment by 1 second. Since we hold the lock
* lastSyncTime always monotonically encreases in the
* atomic sense.
*/
if (lastSyncTime <= lastModifiedTime) {
lastSyncTime = lastModifiedTime + 1000;
prefsFile.setLastModified(lastSyncTime);
}
changeLog.clear();
}
}
public void flush() throws BackingStoreException {
if (isRemoved())
return;
sync();
}
protected void flushSpi() throws BackingStoreException {
// assert false;
}
/**
* Returns true if the specified character is appropriate for use in
* Unix directory names. A character is appropriate if it's a printable
* ASCII character (> 0x1f && < 0x7f) and unequal to slash ('/', 0x2f),
* dot ('.', 0x2e), or underscore ('_', 0x5f).
*/
private static boolean isDirChar(char ch) {
return ch > 0x1f && ch < 0x7f && ch != '/' && ch != '.' && ch != '_';
}
/**
* Returns the directory name corresponding to the specified node name.
* Generally, this is just the node name. If the node name includes
* inappropriate characters (as per isDirChar) it is translated to Base64.
* with the underscore character ('_', 0x5f) prepended.
*/
private static String dirName(String nodeName) {
for (int i=0, n=nodeName.length(); i < n; i++)
if (!isDirChar(nodeName.charAt(i)))
return "_" + Base64.byteArrayToAltBase64(byteArray(nodeName));
return nodeName;
}
/**
* Translate a string into a byte array by translating each character
* into two bytes, high-byte first ("big-endian").
*/
private static byte[] byteArray(String s) {
int len = s.length();
byte[] result = new byte[2*len];
for (int i=0, j=0; i<len; i++) {
char c = s.charAt(i);
result[j++] = (byte) (c>>8);
result[j++] = (byte) c;
}
return result;
}
/**
* Returns the node name corresponding to the specified directory name.
* (Inverts the transformation of dirName(String).
*/
private static String nodeName(String dirName) {
if (dirName.charAt(0) != '_')
return dirName;
byte a[] = Base64.altBase64ToByteArray(dirName.substring(1));
StringBuffer result = new StringBuffer(a.length/2);
for (int i = 0; i < a.length; ) {
int highByte = a[i++] & 0xff;
int lowByte = a[i++] & 0xff;
result.append((char) ((highByte << 8) | lowByte));
}
return result.toString();
}
/**
* Try to acquire the appropriate file lock (user or system). If
* the initial attempt fails, several more attempts are made using
* an exponential backoff strategy. If all attempts fail, this method
* returns false.
* @throws SecurityException if file access denied.
*/
private boolean lockFile(boolean shared) throws SecurityException{
boolean usernode = isUserNode();
int[] result;
int errorCode = 0;
File lockFile = (usernode ? userLockFile : systemLockFile);
long sleepTime = INIT_SLEEP_TIME;
for (int i = 0; i < MAX_ATTEMPTS; i++) {
try {
int perm = (usernode? USER_READ_WRITE: USER_RW_ALL_READ);
result = lockFile0(lockFile.getCanonicalPath(), perm, shared);
errorCode = result[ERROR_CODE];
if (result[LOCK_HANDLE] != 0) {
if (usernode) {
userRootLockHandle = result[LOCK_HANDLE];
} else {
systemRootLockHandle = result[LOCK_HANDLE];
}
return true;
}
} catch(IOException e) {
// // If at first, you don't succeed...
}
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
// Don't lose the interrupt.
Thread.currentThread().interrupt();
checkLockFile0ErrorCode(errorCode);
return false;
}
sleepTime *= 2;
}
checkLockFile0ErrorCode(errorCode);
return false;
}
/**
* Checks if unlockFile0() returned an error. Throws a SecurityException,
* if access denied. Logs a warning otherwise.
*/
private void checkLockFile0ErrorCode (int errorCode)
throws SecurityException {
if (errorCode == EACCES)
throw new SecurityException("Could not lock " +
(isUserNode()? "User prefs." : "System prefs.") +
" Lock file access denied.");
if (errorCode != EAGAIN)
getLogger().warning("Could not lock " +
(isUserNode()? "User prefs. " : "System prefs.") +
" Unix error code " + errorCode + ".");
}
/**
* Locks file using UNIX file locking.
* @param fileName Absolute file name of the lock file.
* @return Returns a lock handle, used to unlock the file.
*/
private static native int[]
lockFile0(String fileName, int permission, boolean shared);
/**
* Unlocks file previously locked by lockFile0().
* @param lockHandle Handle to the file lock.
* @return Returns zero if OK, UNIX error code if failure.
*/
private static native int unlockFile0(int lockHandle);
/**
* Changes UNIX file permissions.
*/
private static native int chmod(String fileName, int permission);
/**
* Initial time between lock attempts, in ms. The time is doubled
* after each failing attempt (except the first).
*/
private static final int INIT_SLEEP_TIME = 50;
/**
* Maximum number of lock attempts.
*/
private static final int MAX_ATTEMPTS = 5;
/**
* Release the appropriate file lock (user or system).
* @throws SecurityException if file access denied.
*/
private void unlockFile() {
int result;
boolean usernode = isUserNode();
File lockFile = (usernode ? userLockFile : systemLockFile);
int lockHandle = ( usernode ? userRootLockHandle:systemRootLockHandle);
if (lockHandle == 0) {
getLogger().warning("Unlock: zero lockHandle for " +
(usernode ? "user":"system") + " preferences.)");
return;
}
result = unlockFile0(lockHandle);
if (result != 0) {
getLogger().warning("Could not drop file-lock on " +
(isUserNode() ? "user" : "system") + " preferences." +
" Unix error code " + result + ".");
if (result == EACCES)
throw new SecurityException("Could not unlock" +
(isUserNode()? "User prefs." : "System prefs.") +
" Lock file access denied.");
}
if (isUserNode()) {
userRootLockHandle = 0;
} else {
systemRootLockHandle = 0;
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
/**
* Factory for FileSystemPreferences. This class allows FileSystemPreferences
* to be installed as the Preferences implementations via the
* java.util.prefs.PreferencesFactory system property.
*
* @author Josh Bloch
* @see FileSystemPreferences
* @see Preferences
* @since 1.4
*/
class FileSystemPreferencesFactory implements PreferencesFactory {
public Preferences userRoot() {
return FileSystemPreferences.getUserRoot();
}
public Preferences systemRoot() {
return FileSystemPreferences.getSystemRoot();
}
}

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Solaris/Linux platform specific code to support the Prefs API.
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <utime.h>
#include "jni_util.h"
#include "java_util_prefs_FileSystemPreferences.h"
/*
* Declare library specific JNI_Onload entry if static build
*/
DEF_STATIC_JNI_OnLoad
JNIEXPORT jint JNICALL
Java_java_util_prefs_FileSystemPreferences_chmod(JNIEnv *env,
jclass thisclass, jstring java_fname, jint permission) {
const char *fname = JNU_GetStringPlatformChars(env, java_fname, NULL);
int result = -1;
if (fname) {
result = chmod(fname, permission);
if (result != 0)
result = errno;
JNU_ReleaseStringPlatformChars(env, java_fname, fname);
}
return (jint) result;
}
/**
* Try to open a named lock file.
* The result is a cookie that can be used later to unlock the file.
* On failure the result is zero.
*/
JNIEXPORT jintArray JNICALL
Java_java_util_prefs_FileSystemPreferences_lockFile0(JNIEnv *env,
jclass thisclass, jstring java_fname, jint permission, jboolean shared) {
const char *fname = JNU_GetStringPlatformChars(env, java_fname, NULL);
int fd, rc;
int result[2] = {0, 0};
jintArray javaResult = NULL;
int old_umask;
struct flock fl;
if (!fname)
return javaResult;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
fl.l_start = 0;
if (shared == JNI_TRUE) {
fl.l_type = F_RDLCK;
} else {
fl.l_type = F_WRLCK;
}
if (shared == JNI_TRUE) {
fd = open(fname, O_RDONLY, 0);
result[1] = errno;
} else {
old_umask = umask(0);
fd = open(fname, O_WRONLY|O_CREAT, permission);
result[1] = errno;
umask(old_umask);
}
if (fd < 0) {
result[0] = 0;
} else {
rc = fcntl(fd, F_SETLK, &fl);
result[1] = errno;
if (rc < 0) {
result[0]= 0;
close(fd);
} else {
result[0] = fd;
}
}
JNU_ReleaseStringPlatformChars(env, java_fname, fname);
javaResult = (*env)->NewIntArray(env,2);
if (javaResult)
(*env)->SetIntArrayRegion(env, javaResult, 0, 2, result);
return javaResult;
}
/**
* Try to unlock a lock file, using a cookie returned by lockFile.
*/
JNIEXPORT jint JNICALL
Java_java_util_prefs_FileSystemPreferences_unlockFile0(JNIEnv *env,
jclass thisclass, jint fd) {
int rc;
struct flock fl;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
fl.l_start = 0;
fl.l_type = F_UNLCK;
rc = fcntl(fd, F_SETLK, &fl);
if (rc < 0) {
close(fd);
return (jint)errno;
}
rc = close(fd);
if (rc < 0) {
return (jint) errno;
}
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.prefs;
/**
* Implementation of {@code PreferencesFactory} to return
* WindowsPreferences objects.
*
* @author Konstantin Kladko
* @see Preferences
* @see WindowsPreferences
* @since 1.4
*/
class WindowsPreferencesFactory implements PreferencesFactory {
/**
* Returns WindowsPreferences.userRoot
*/
public Preferences userRoot() {
return WindowsPreferences.getUserRoot();
}
/**
* Returns WindowsPreferences.systemRoot
*/
public Preferences systemRoot() {
return WindowsPreferences.getSystemRoot();
}
}

View file

@ -0,0 +1,262 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <stdlib.h>
#include <windows.h>
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "java_util_prefs_WindowsPreferences.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Declare library specific JNI_Onload entry if static build
*/
DEF_STATIC_JNI_OnLoad
JNIEXPORT jlongArray JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegOpenKey(JNIEnv* env,
jclass this_class, jlong hKey, jbyteArray lpSubKey, jint securityMask) {
char* str = (*env)->GetByteArrayElements(env, lpSubKey, NULL);
CHECK_NULL_RETURN(str, NULL);
HKEY handle;
int errorCode = RegOpenKeyEx((HKEY) hKey, str, 0, securityMask, &handle);
(*env)->ReleaseByteArrayElements(env, lpSubKey, str, 0);
_Alignas(8) jlong tmp[2];
tmp[0] = (jlong) handle;
tmp[1] = errorCode;
jlongArray result = (*env)->NewLongArray(env, 2);
if (result != NULL) {
(*env)->SetLongArrayRegion(env, result, 0, 2, tmp);
}
return result;
}
JNIEXPORT jint JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegCloseKey(JNIEnv* env,
jclass this_class, jlong hKey) {
return (jint) RegCloseKey((HKEY) hKey);
};
JNIEXPORT jlongArray JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegCreateKeyEx(JNIEnv* env,
jclass this_class, jlong hKey, jbyteArray lpSubKey) {
char* str = (*env)->GetByteArrayElements(env, lpSubKey, NULL);
CHECK_NULL_RETURN(str, NULL);
HKEY handle;
DWORD lpdwDisposition;
int errorCode = RegCreateKeyEx((HKEY) hKey, str, 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_READ,
NULL, &handle, &lpdwDisposition);
(*env)->ReleaseByteArrayElements(env, lpSubKey, str, 0);
_Alignas(8) jlong tmp[3];
tmp[0] = (jlong) handle;
tmp[1] = errorCode;
tmp[2] = lpdwDisposition;
jlongArray result = (*env)->NewLongArray(env, 3);
if (result != NULL) {
(*env)->SetLongArrayRegion(env, result, 0, 3, tmp);
}
return result;
}
JNIEXPORT jint JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegDeleteKey(JNIEnv* env,
jclass this_class, jlong hKey, jbyteArray lpSubKey) {
char* str = (*env)->GetByteArrayElements(env, lpSubKey, NULL);
CHECK_NULL_RETURN(str, -1);
int result = RegDeleteKey((HKEY) hKey, str);
(*env)->ReleaseByteArrayElements(env, lpSubKey, str, 0);
return result;
};
JNIEXPORT jint JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegFlushKey(JNIEnv* env,
jclass this_class, jlong hKey) {
return RegFlushKey((HKEY) hKey);
}
JNIEXPORT jbyteArray JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegQueryValueEx(JNIEnv* env,
jclass this_class, jlong hKey, jbyteArray valueName) {
char* valueNameStr = (*env)->GetByteArrayElements(env, valueName, NULL);
CHECK_NULL_RETURN(valueNameStr, NULL);
DWORD valueType;
DWORD valueSize;
if (RegQueryValueEx((HKEY) hKey, valueNameStr, NULL, &valueType, NULL,
&valueSize) != ERROR_SUCCESS) {
(*env)->ReleaseByteArrayElements(env, valueName, valueNameStr, 0);
return NULL;
}
char* buffer = (char*) malloc(valueSize);
if (buffer != NULL) {
if (RegQueryValueEx((HKEY) hKey, valueNameStr, NULL, &valueType, buffer,
&valueSize) != ERROR_SUCCESS) {
free(buffer);
(*env)->ReleaseByteArrayElements(env, valueName, valueNameStr, 0);
return NULL;
}
} else {
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
(*env)->ReleaseByteArrayElements(env, valueName, valueNameStr, 0);
return NULL;
}
jbyteArray result;
if (valueType == REG_SZ) {
result = (*env)->NewByteArray(env, valueSize);
if (result != NULL) {
(*env)->SetByteArrayRegion(env, result, 0, valueSize, buffer);
}
} else {
result = NULL;
}
free(buffer);
(*env)->ReleaseByteArrayElements(env, valueName, valueNameStr, 0);
return result;
}
JNIEXPORT jint JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegSetValueEx(JNIEnv* env,
jclass this_class, jlong hKey, jbyteArray valueName, jbyteArray data) {
if ((valueName == NULL) || (data == NULL)) {
return -1;
}
int size = (*env)->GetArrayLength(env, data);
char* dataStr = (*env)->GetByteArrayElements(env, data, NULL);
CHECK_NULL_RETURN(dataStr, -1);
char* valueNameStr = (*env)->GetByteArrayElements(env, valueName, NULL);
int error_code = -1;
if (valueNameStr != NULL) {
error_code = RegSetValueEx((HKEY) hKey, valueNameStr, 0,
REG_SZ, dataStr, size);
(*env)->ReleaseByteArrayElements(env, valueName, valueNameStr, 0);
}
(*env)->ReleaseByteArrayElements(env, data, dataStr, 0);
return error_code;
}
JNIEXPORT jint JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegDeleteValue(JNIEnv* env,
jclass this_class, jlong hKey, jbyteArray valueName) {
if (valueName == NULL) {
return -1;
}
char* valueNameStr = (*env)->GetByteArrayElements(env, valueName, NULL);
CHECK_NULL_RETURN(valueNameStr, -1);
int error_code = RegDeleteValue((HKEY) hKey, valueNameStr);
(*env)->ReleaseByteArrayElements(env, valueName, valueNameStr, 0);
return error_code;
}
JNIEXPORT jlongArray JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegQueryInfoKey(JNIEnv* env,
jclass this_class, jlong hKey) {
int subKeysNumber;
int maxSubKeyLength;
int valuesNumber;
int maxValueNameLength;
int errorCode = RegQueryInfoKey((HKEY) hKey, NULL, NULL, NULL,
&subKeysNumber, &maxSubKeyLength, NULL,
&valuesNumber, &maxValueNameLength,
NULL, NULL, NULL);
_Alignas(8) jlong tmp[5];
tmp[0] = subKeysNumber;
tmp[1] = errorCode;
tmp[2] = valuesNumber;
tmp[3] = maxSubKeyLength;
tmp[4] = maxValueNameLength;
jintArray result = (*env)->NewLongArray(env, 5);
if (result != NULL) {
(*env)->SetLongArrayRegion(env, result, 0, 5, tmp);
}
return result;
}
JNIEXPORT jbyteArray JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegEnumKeyEx(JNIEnv* env,
jclass this_class, jlong hKey, jint subKeyIndex, jint maxKeyLength) {
int size = maxKeyLength;
char* buffer = (char*) malloc(maxKeyLength);
if (buffer == NULL) {
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
return NULL;
}
if (RegEnumKeyEx((HKEY) hKey, subKeyIndex, buffer, &size, NULL, NULL,
NULL, NULL) != ERROR_SUCCESS) {
free(buffer);
return NULL;
}
jbyteArray result = (*env)->NewByteArray(env, size + 1);
if (result != NULL) {
(*env)->SetByteArrayRegion(env, result, 0, size + 1, buffer);
}
free(buffer);
return result;
}
JNIEXPORT jbyteArray JNICALL
Java_java_util_prefs_WindowsPreferences_WindowsRegEnumValue(JNIEnv* env,
jclass this_class, jlong hKey, jint valueIndex, jint maxValueNameLength) {
int size = maxValueNameLength;
char* buffer = (char*) malloc(maxValueNameLength);
if (buffer == NULL) {
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
return NULL;
}
int error_code = RegEnumValue((HKEY) hKey, valueIndex, buffer,
&size, NULL, NULL, NULL, NULL);
if (error_code != ERROR_SUCCESS) {
free(buffer);
return NULL;
}
jbyteArray result = (*env)->NewByteArray(env, size + 1);
if (result != NULL) {
(*env)->SetByteArrayRegion(env, result, 0, size + 1, buffer);
}
free(buffer);
return result;
}
#ifdef __cplusplus
}
#endif