Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/classes/java/util/prefs/MacOSXPreferencesFile.java
38918 views
/*1* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util.prefs;2627import java.util.HashMap;28import java.util.HashSet;29import java.util.Iterator;30import java.util.Timer;31import java.util.TimerTask;32import java.lang.ref.WeakReference;333435/*36MacOSXPreferencesFile synchronization:3738Everything is synchronized on MacOSXPreferencesFile.class. This prevents:39* simultaneous updates to cachedFiles or changedFiles40* simultaneous creation of two objects for the same name+user+host triplet41* simultaneous modifications to the same file42* modifications during syncWorld/flushWorld43* (in MacOSXPreferences.removeNodeSpi()) modification or sync during44multi-step node removal process45... among other things.46*/47/*48Timers. There are two timers that control synchronization of prefs data to49and from disk.5051* Sync timer periodically calls syncWorld() to force external disk changes52(e.g. from another VM) into the memory cache. The sync timer runs even53if there are no outstanding local changes. The sync timer syncs all live54MacOSXPreferencesFile objects (the cachedFiles list).55The sync timer period is controlled by the java.util.prefs.syncInterval56property (same as FileSystemPreferences). By default there is *no*57sync timer (unlike FileSystemPreferences); it is only enabled if the58syncInterval property is set. The minimum interval is 5 seconds.5960* Flush timer calls flushWorld() to force local changes to disk.61The flush timer is scheduled to fire some time after each pref change,62unless it's already scheduled to fire before that. syncWorld and63flushWorld will cancel any outstanding flush timer as unnecessary.64The flush timer flushes all changed files (the changedFiles list).65The time between pref write and flush timer call is controlled by the66java.util.prefs.flushDelay property (unlike FileSystemPreferences).67The default is 60 seconds and the minimum is 5 seconds.6869The flush timer's behavior is required by the Java Preferences spec70("changes will eventually propagate to the persistent backing store with71an implementation-dependent delay"). The sync timer is not required by72the spec (multiple VMs are only required to not corrupt the prefs), but73the periodic sync is implemented by FileSystemPreferences and may be74useful to some programs. The sync timer is disabled by default because75it's expensive and is usually not necessary.76*/7778class MacOSXPreferencesFile {7980static {81java.security.AccessController.doPrivileged(82new java.security.PrivilegedAction<Void>() {83public Void run() {84System.loadLibrary("osx");85return null;86}87});88}8990private class FlushTask extends TimerTask {91public void run() {92MacOSXPreferencesFile.flushWorld();93}94}9596private class SyncTask extends TimerTask {97public void run() {98MacOSXPreferencesFile.syncWorld();99}100}101102// Maps string -> weak reference to MacOSXPreferencesFile103private static HashMap<String, WeakReference<MacOSXPreferencesFile>>104cachedFiles;105// Files that may have unflushed changes106private static HashSet<MacOSXPreferencesFile> changedFiles;107108109// Timer and pending sync and flush tasks (which are both scheduled110// on the same timer)111private static Timer timer = null;112private static FlushTask flushTimerTask = null;113private static long flushDelay = -1; // in seconds (min 5, default 60)114private static long syncInterval = -1; // (min 5, default negative == off)115116private String appName;117private long user;118private long host;119120String name() { return appName; }121long user() { return user; }122long host() { return host; }123124// private constructor - use factory method getFile() instead125private MacOSXPreferencesFile(String newName, long newUser, long newHost)126{127appName = newName;128user = newUser;129host = newHost;130}131132// Factory method133// Always returns the same object for the given name+user+host134static synchronized MacOSXPreferencesFile135getFile(String newName, boolean isUser)136{137MacOSXPreferencesFile result = null;138139if (cachedFiles == null)140cachedFiles = new HashMap<>();141142String hashkey =143newName + String.valueOf(isUser);144WeakReference<MacOSXPreferencesFile> hashvalue = cachedFiles.get(hashkey);145if (hashvalue != null) {146result = hashvalue.get();147}148if (result == null) {149// Java user node == CF current user, any host150// Java system node == CF any user, current host151result = new MacOSXPreferencesFile(newName,152isUser ? cfCurrentUser : cfAnyUser,153isUser ? cfAnyHost : cfCurrentHost);154cachedFiles.put(hashkey, new WeakReference<MacOSXPreferencesFile>(result));155}156157// Don't schedule this file for flushing until some nodes or158// keys are added to it.159160// Do set up the sync timer if requested; sync timer affects reads161// as well as writes.162initSyncTimerIfNeeded();163164return result;165}166167168// Write all prefs changes to disk and clear all cached prefs values169// (so the next read will read from disk).170static synchronized boolean syncWorld()171{172boolean ok = true;173174if (cachedFiles != null && !cachedFiles.isEmpty()) {175Iterator<WeakReference<MacOSXPreferencesFile>> iter =176cachedFiles.values().iterator();177while (iter.hasNext()) {178WeakReference<MacOSXPreferencesFile> ref = iter.next();179MacOSXPreferencesFile f = ref.get();180if (f != null) {181if (!f.synchronize()) ok = false;182} else {183iter.remove();184}185}186}187188// Kill any pending flush189if (flushTimerTask != null) {190flushTimerTask.cancel();191flushTimerTask = null;192}193194// Clear changed file list. The changed files were guaranteed to195// have been in the cached file list (because there was a strong196// reference from changedFiles.197if (changedFiles != null) changedFiles.clear();198199return ok;200}201202203// Sync only current user preferences204static synchronized boolean syncUser() {205boolean ok = true;206if (cachedFiles != null && !cachedFiles.isEmpty()) {207Iterator<WeakReference<MacOSXPreferencesFile>> iter =208cachedFiles.values().iterator();209while (iter.hasNext()) {210WeakReference<MacOSXPreferencesFile> ref = iter.next();211MacOSXPreferencesFile f = ref.get();212if (f != null && f.user == cfCurrentUser) {213if (!f.synchronize()) {214ok = false;215}216} else {217iter.remove();218}219}220}221// Remove synchronized file from changed file list. The changed files were222// guaranteed to have been in the cached file list (because there was a strong223// reference from changedFiles.224if (changedFiles != null) {225Iterator<MacOSXPreferencesFile> iterChanged = changedFiles.iterator();226while (iterChanged.hasNext()) {227MacOSXPreferencesFile f = iterChanged.next();228if (f != null && f.user == cfCurrentUser)229iterChanged.remove();230}231}232return ok;233}234235//Flush only current user preferences236static synchronized boolean flushUser() {237boolean ok = true;238if (changedFiles != null && !changedFiles.isEmpty()) {239Iterator<MacOSXPreferencesFile> iterator = changedFiles.iterator();240while(iterator.hasNext()) {241MacOSXPreferencesFile f = iterator.next();242if (f.user == cfCurrentUser) {243if (!f.synchronize())244ok = false;245else246iterator.remove();247}248}249}250return ok;251}252253// Write all prefs changes to disk, but do not clear all cached prefs254// values. Also kills any scheduled flush task.255// There's no CFPreferencesFlush() (<rdar://problem/3049129>), so lots of cached prefs256// are cleared anyway.257static synchronized boolean flushWorld()258{259boolean ok = true;260261if (changedFiles != null && !changedFiles.isEmpty()) {262for (MacOSXPreferencesFile f : changedFiles) {263if (!f.synchronize())264ok = false;265}266changedFiles.clear();267}268269if (flushTimerTask != null) {270flushTimerTask.cancel();271flushTimerTask = null;272}273274return ok;275}276277// Mark this prefs file as changed. The changes will be flushed in278// at most flushDelay() seconds.279// Must be called when synchronized on MacOSXPreferencesFile.class280private void markChanged()281{282// Add this file to the changed file list283if (changedFiles == null)284changedFiles = new HashSet<>();285changedFiles.add(this);286287// Schedule a new flush and a shutdown hook, if necessary288if (flushTimerTask == null) {289flushTimerTask = new FlushTask();290timer().schedule(flushTimerTask, flushDelay() * 1000);291}292}293294// Return the flush delay, initializing from a property if necessary.295private static synchronized long flushDelay()296{297if (flushDelay == -1) {298try {299// flush delay >= 5, default 60300flushDelay = Math.max(5, Integer.parseInt(System.getProperty("java.util.prefs.flushDelay", "60")));301} catch (NumberFormatException e) {302flushDelay = 60;303}304}305return flushDelay;306}307308// Initialize and run the sync timer, if the sync timer property is set309// and the sync timer hasn't already been started.310private static synchronized void initSyncTimerIfNeeded()311{312// syncInterval: -1 is uninitialized, other negative is off,313// positive is seconds between syncs (min 5).314315if (syncInterval == -1) {316try {317syncInterval = Integer.parseInt(System.getProperty("java.util.prefs.syncInterval", "-2"));318if (syncInterval >= 0) {319// minimum of 5 seconds320syncInterval = Math.max(5, syncInterval);321} else {322syncInterval = -2; // default off323}324} catch (NumberFormatException e) {325syncInterval = -2; // bad property value - default off326}327328if (syncInterval > 0) {329timer().schedule(new TimerTask() {330@Override331public void run() {332MacOSXPreferencesFile.syncWorld();}333}, syncInterval * 1000, syncInterval * 1000);334} else {335// syncInterval property not set. No sync timer ever.336}337}338}339340// Return the timer used for flush and sync, creating it if necessary.341private static synchronized Timer timer()342{343if (timer == null) {344timer = new Timer(true); // daemon345Thread flushThread = new Thread() {346@Override347public void run() {348flushWorld();349}350};351/* Set context class loader to null in order to avoid352* keeping a strong reference to an application classloader.353*/354flushThread.setContextClassLoader(null);355Runtime.getRuntime().addShutdownHook(flushThread);356}357return timer;358}359360361// Node manipulation362boolean addNode(String path)363{364synchronized(MacOSXPreferencesFile.class) {365markChanged();366return addNode(path, appName, user, host);367}368}369370void removeNode(String path)371{372synchronized(MacOSXPreferencesFile.class) {373markChanged();374removeNode(path, appName, user, host);375}376}377378boolean addChildToNode(String path, String child)379{380synchronized(MacOSXPreferencesFile.class) {381markChanged();382return addChildToNode(path, child+"/", appName, user, host);383}384}385386void removeChildFromNode(String path, String child)387{388synchronized(MacOSXPreferencesFile.class) {389markChanged();390removeChildFromNode(path, child+"/", appName, user, host);391}392}393394395// Key manipulation396void addKeyToNode(String path, String key, String value)397{398synchronized(MacOSXPreferencesFile.class) {399markChanged();400addKeyToNode(path, key, value, appName, user, host);401}402}403404void removeKeyFromNode(String path, String key)405{406synchronized(MacOSXPreferencesFile.class) {407markChanged();408removeKeyFromNode(path, key, appName, user, host);409}410}411412String getKeyFromNode(String path, String key)413{414synchronized(MacOSXPreferencesFile.class) {415return getKeyFromNode(path, key, appName, user, host);416}417}418419420// Enumerators421String[] getChildrenForNode(String path)422{423synchronized(MacOSXPreferencesFile.class) {424return getChildrenForNode(path, appName, user, host);425}426}427428String[] getKeysForNode(String path)429{430synchronized(MacOSXPreferencesFile.class) {431return getKeysForNode(path, appName, user, host);432}433}434435436// Synchronization437boolean synchronize()438{439synchronized(MacOSXPreferencesFile.class) {440return synchronize(appName, user, host);441}442}443444445// CF functions446// Must be called when synchronized on MacOSXPreferencesFile.class447private static final native boolean448addNode(String path, String name, long user, long host);449private static final native void450removeNode(String path, String name, long user, long host);451private static final native boolean452addChildToNode(String path, String child,453String name, long user, long host);454private static final native void455removeChildFromNode(String path, String child,456String name, long user, long host);457private static final native void458addKeyToNode(String path, String key, String value,459String name, long user, long host);460private static final native void461removeKeyFromNode(String path, String key,462String name, long user, long host);463private static final native String464getKeyFromNode(String path, String key,465String name, long user, long host);466private static final native String[]467getChildrenForNode(String path, String name, long user, long host);468private static final native String[]469getKeysForNode(String path, String name, long user, long host);470private static final native boolean471synchronize(String name, long user, long host);472473// CFPreferences host and user values (CFStringRefs)474private static long cfCurrentUser = currentUser();475private static long cfAnyUser = anyUser();476private static long cfCurrentHost = currentHost();477private static long cfAnyHost = anyHost();478479// CFPreferences constant accessors480private static final native long currentUser();481private static final native long anyUser();482private static final native long currentHost();483private static final native long anyHost();484}485486487488