84 public Void run() {
85 System.loadLibrary("osx");
86 return null;
87 }
88 });
89 }
90
91 private class FlushTask extends TimerTask {
92 public void run() {
93 MacOSXPreferencesFile.flushWorld();
94 }
95 }
96
97 private class SyncTask extends TimerTask {
98 public void run() {
99 MacOSXPreferencesFile.syncWorld();
100 }
101 }
102
103 // Maps string -> weak reference to MacOSXPreferencesFile
104 private static HashMap cachedFiles = null;
105 // Files that may have unflushed changes
106 private static HashSet changedFiles = null;
107
108
109 // Timer and pending sync and flush tasks (which are both scheduled
110 // on the same timer)
111 private static Timer timer = null;
112 private static FlushTask flushTimerTask = null;
113 private static long flushDelay = -1; // in seconds (min 5, default 60)
114 private static long syncInterval = -1; // (min 5, default negative == off)
115
116 private String appName;
117 private long user;
118 private long host;
119
120 String name() { return appName; }
121 long user() { return user; }
122 long host() { return host; }
123
124 // private contructor - use factory method getFile() instead
125 private MacOSXPreferencesFile(String newName, long newUser, long newHost)
126 {
127 appName = newName;
128 user = newUser;
129 host = newHost;
130 }
131
132 // Factory method
133 // Always returns the same object for the given name+user+host
134 static synchronized MacOSXPreferencesFile
135 getFile(String newName, boolean isUser)
136 {
137 MacOSXPreferencesFile result = null;
138
139 if (cachedFiles == null) cachedFiles = new HashMap();
140
141 String hashkey =
142 newName + String.valueOf(isUser);
143 WeakReference hashvalue = (WeakReference)cachedFiles.get(hashkey);
144 if (hashvalue != null) {
145 result = (MacOSXPreferencesFile)hashvalue.get();
146 }
147 if (result == null) {
148 // Java user node == CF current user, any host
149 // Java system node == CF any user, current host
150 result = new MacOSXPreferencesFile(newName,
151 isUser ? cfCurrentUser : cfAnyUser,
152 isUser ? cfAnyHost : cfCurrentHost);
153 cachedFiles.put(hashkey, new WeakReference(result));
154 }
155
156 // Don't schedule this file for flushing until some nodes or
157 // keys are added to it.
158
159 // Do set up the sync timer if requested; sync timer affects reads
160 // as well as writes.
161 initSyncTimerIfNeeded();
162
163 return result;
164 }
165
166
167 // Write all prefs changes to disk and clear all cached prefs values
168 // (so the next read will read from disk).
169 static synchronized boolean syncWorld()
170 {
171 boolean ok = true;
172
173 if (cachedFiles != null && !cachedFiles.isEmpty()) {
174 Iterator iter = cachedFiles.values().iterator();
175 while (iter.hasNext()) {
176 WeakReference ref = (WeakReference)iter.next();
177 MacOSXPreferencesFile f = (MacOSXPreferencesFile)ref.get();
178 if (f != null) {
179 if (!f.synchronize()) ok = false;
180 } else {
181 iter.remove();
182 }
183 }
184 }
185
186 // Kill any pending flush
187 if (flushTimerTask != null) {
188 flushTimerTask.cancel();
189 flushTimerTask = null;
190 }
191
192 // Clear changed file list. The changed files were guaranteed to
193 // have been in the cached file list (because there was a strong
194 // reference from changedFiles.
195 if (changedFiles != null) changedFiles.clear();
196
197 return ok;
198 }
199
200
201 // Sync only current user preferences
202 static synchronized boolean syncUser() {
203 boolean ok = true;
204 if (cachedFiles != null && !cachedFiles.isEmpty()) {
205 Iterator<WeakReference> iter = cachedFiles.values().iterator();
206 while (iter.hasNext()) {
207 WeakReference ref = iter.next();
208 MacOSXPreferencesFile f = (MacOSXPreferencesFile)ref.get();
209 if (f != null && f.user == cfCurrentUser) {
210 if (!f.synchronize()) {
211 ok = false;
212 }
213 } else {
214 iter.remove();
215 }
216 }
217 }
218 // Remove synchronized file from changed file list. The changed files were
219 // guaranteed to have been in the cached file list (because there was a strong
220 // reference from changedFiles.
221 if (changedFiles != null) {
222 Iterator<MacOSXPreferencesFile> iterChanged = changedFiles.iterator();
223 while (iterChanged.hasNext()) {
224 MacOSXPreferencesFile f = iterChanged.next();
225 if (f != null && f.user == cfCurrentUser)
226 iterChanged.remove();
227 }
228 }
229 return ok;
230 }
231
232
233
234 // Write all prefs changes to disk, but do not clear all cached prefs
235 // values. Also kills any scheduled flush task.
236 // There's no CFPreferencesFlush() (<rdar://problem/3049129>), so lots of cached prefs
237 // are cleared anyway.
238 static synchronized boolean flushWorld()
239 {
240 boolean ok = true;
241
242 if (changedFiles != null && !changedFiles.isEmpty()) {
243 Iterator iter = changedFiles.iterator();
244 while (iter.hasNext()) {
245 MacOSXPreferencesFile f = (MacOSXPreferencesFile)iter.next();
246 if (!f.synchronize()) ok = false;
247 }
248
249 changedFiles.clear();
250 }
251
252 if (flushTimerTask != null) {
253 flushTimerTask.cancel();
254 flushTimerTask = null;
255 }
256
257 return ok;
258 }
259
260 // Mark this prefs file as changed. The changes will be flushed in
261 // at most flushDelay() seconds.
262 // Must be called when synchronized on MacOSXPreferencesFile.class
263 private void markChanged()
264 {
265 // Add this file to the changed file list
266 if (changedFiles == null) changedFiles = new HashSet();
267 changedFiles.add(this);
268
269 // Schedule a new flush and a shutdown hook, if necessary
270 if (flushTimerTask == null) {
271 flushTimerTask = new FlushTask();
272 timer().schedule(flushTimerTask, flushDelay() * 1000);
273 }
274 }
275
276 // Return the flush delay, initializing from a property if necessary.
277 private static synchronized long flushDelay()
278 {
279 if (flushDelay == -1) {
280 try {
281 // flush delay >= 5, default 60
282 flushDelay = Math.max(5, Integer.parseInt(System.getProperty("java.util.prefs.flushDelay", "60")));
283 } catch (NumberFormatException e) {
284 flushDelay = 60;
285 }
286 }
292 private static synchronized void initSyncTimerIfNeeded()
293 {
294 // syncInterval: -1 is uninitialized, other negative is off,
295 // positive is seconds between syncs (min 5).
296
297 if (syncInterval == -1) {
298 try {
299 syncInterval = Integer.parseInt(System.getProperty("java.util.prefs.syncInterval", "-2"));
300 if (syncInterval >= 0) {
301 // minimum of 5 seconds
302 syncInterval = Math.max(5, syncInterval);
303 } else {
304 syncInterval = -2; // default off
305 }
306 } catch (NumberFormatException e) {
307 syncInterval = -2; // bad property value - default off
308 }
309
310 if (syncInterval > 0) {
311 timer().schedule(new TimerTask() {
312 public void run() { MacOSXPreferencesFile.syncWorld();}
313 }, syncInterval * 1000, syncInterval * 1000);
314 } else {
315 // syncInterval property not set. No sync timer ever.
316 }
317 }
318 }
319
320 // Return the timer used for flush and sync, creating it if necessary.
321 private static synchronized Timer timer()
322 {
323 if (timer == null) {
324 timer = new Timer(true); // daemon
325 Thread flushThread = new Thread() {
326 public void run() {
327 flushWorld();
328 }
329 };
330 /* Set context class loader to null in order to avoid
331 * keeping a strong reference to an application classloader.
332 */
333 flushThread.setContextClassLoader(null);
334 Runtime.getRuntime().addShutdownHook(flushThread);
335 }
336 return timer;
337 }
338
339
340 // Node manipulation
341 boolean addNode(String path)
342 {
343 synchronized(MacOSXPreferencesFile.class) {
344 markChanged();
345 return addNode(path, appName, user, host);
|
84 public Void run() {
85 System.loadLibrary("osx");
86 return null;
87 }
88 });
89 }
90
91 private class FlushTask extends TimerTask {
92 public void run() {
93 MacOSXPreferencesFile.flushWorld();
94 }
95 }
96
97 private class SyncTask extends TimerTask {
98 public void run() {
99 MacOSXPreferencesFile.syncWorld();
100 }
101 }
102
103 // Maps string -> weak reference to MacOSXPreferencesFile
104 private static HashMap<String, WeakReference<MacOSXPreferencesFile>>
105 cachedFiles = null;
106 // Files that may have unflushed changes
107 private static HashSet<MacOSXPreferencesFile> changedFiles = null;
108
109
110 // Timer and pending sync and flush tasks (which are both scheduled
111 // on the same timer)
112 private static Timer timer = null;
113 private static FlushTask flushTimerTask = null;
114 private static long flushDelay = -1; // in seconds (min 5, default 60)
115 private static long syncInterval = -1; // (min 5, default negative == off)
116
117 private String appName;
118 private long user;
119 private long host;
120
121 String name() { return appName; }
122 long user() { return user; }
123 long host() { return host; }
124
125 // private contructor - use factory method getFile() instead
126 private MacOSXPreferencesFile(String newName, long newUser, long newHost)
127 {
128 appName = newName;
129 user = newUser;
130 host = newHost;
131 }
132
133 // Factory method
134 // Always returns the same object for the given name+user+host
135 static synchronized MacOSXPreferencesFile
136 getFile(String newName, boolean isUser)
137 {
138 MacOSXPreferencesFile result = null;
139
140 if (cachedFiles == null)
141 cachedFiles = new HashMap<>();
142
143 String hashkey =
144 newName + String.valueOf(isUser);
145 WeakReference<MacOSXPreferencesFile> hashvalue = cachedFiles.get(hashkey);
146 if (hashvalue != null) {
147 result = hashvalue.get();
148 }
149 if (result == null) {
150 // Java user node == CF current user, any host
151 // Java system node == CF any user, current host
152 result = new MacOSXPreferencesFile(newName,
153 isUser ? cfCurrentUser : cfAnyUser,
154 isUser ? cfAnyHost : cfCurrentHost);
155 cachedFiles.put(hashkey, new WeakReference<MacOSXPreferencesFile>(result));
156 }
157
158 // Don't schedule this file for flushing until some nodes or
159 // keys are added to it.
160
161 // Do set up the sync timer if requested; sync timer affects reads
162 // as well as writes.
163 initSyncTimerIfNeeded();
164
165 return result;
166 }
167
168
169 // Write all prefs changes to disk and clear all cached prefs values
170 // (so the next read will read from disk).
171 static synchronized boolean syncWorld()
172 {
173 boolean ok = true;
174
175 if (cachedFiles != null && !cachedFiles.isEmpty()) {
176 Iterator<WeakReference<MacOSXPreferencesFile>> iter =
177 cachedFiles.values().iterator();
178 while (iter.hasNext()) {
179 WeakReference<MacOSXPreferencesFile> ref = iter.next();
180 MacOSXPreferencesFile f = ref.get();
181 if (f != null) {
182 if (!f.synchronize()) ok = false;
183 } else {
184 iter.remove();
185 }
186 }
187 }
188
189 // Kill any pending flush
190 if (flushTimerTask != null) {
191 flushTimerTask.cancel();
192 flushTimerTask = null;
193 }
194
195 // Clear changed file list. The changed files were guaranteed to
196 // have been in the cached file list (because there was a strong
197 // reference from changedFiles.
198 if (changedFiles != null) changedFiles.clear();
199
200 return ok;
201 }
202
203
204 // Sync only current user preferences
205 static synchronized boolean syncUser() {
206 boolean ok = true;
207 if (cachedFiles != null && !cachedFiles.isEmpty()) {
208 Iterator<WeakReference<MacOSXPreferencesFile>> iter =
209 cachedFiles.values().iterator();
210 while (iter.hasNext()) {
211 WeakReference<MacOSXPreferencesFile> ref = iter.next();
212 MacOSXPreferencesFile f = ref.get();
213 if (f != null && f.user == cfCurrentUser) {
214 if (!f.synchronize()) {
215 ok = false;
216 }
217 } else {
218 iter.remove();
219 }
220 }
221 }
222 // Remove synchronized file from changed file list. The changed files were
223 // guaranteed to have been in the cached file list (because there was a strong
224 // reference from changedFiles.
225 if (changedFiles != null) {
226 Iterator<MacOSXPreferencesFile> iterChanged = changedFiles.iterator();
227 while (iterChanged.hasNext()) {
228 MacOSXPreferencesFile f = iterChanged.next();
229 if (f != null && f.user == cfCurrentUser)
230 iterChanged.remove();
231 }
232 }
233 return ok;
234 }
235
236
237
238 // Write all prefs changes to disk, but do not clear all cached prefs
239 // values. Also kills any scheduled flush task.
240 // There's no CFPreferencesFlush() (<rdar://problem/3049129>), so lots of cached prefs
241 // are cleared anyway.
242 static synchronized boolean flushWorld()
243 {
244 boolean ok = true;
245
246 if (changedFiles != null && !changedFiles.isEmpty()) {
247 for (MacOSXPreferencesFile f : changedFiles) {
248 if (!f.synchronize())
249 ok = false;
250 }
251 changedFiles.clear();
252 }
253
254 if (flushTimerTask != null) {
255 flushTimerTask.cancel();
256 flushTimerTask = null;
257 }
258
259 return ok;
260 }
261
262 // Mark this prefs file as changed. The changes will be flushed in
263 // at most flushDelay() seconds.
264 // Must be called when synchronized on MacOSXPreferencesFile.class
265 private void markChanged()
266 {
267 // Add this file to the changed file list
268 if (changedFiles == null)
269 changedFiles = new HashSet<>();
270 changedFiles.add(this);
271
272 // Schedule a new flush and a shutdown hook, if necessary
273 if (flushTimerTask == null) {
274 flushTimerTask = new FlushTask();
275 timer().schedule(flushTimerTask, flushDelay() * 1000);
276 }
277 }
278
279 // Return the flush delay, initializing from a property if necessary.
280 private static synchronized long flushDelay()
281 {
282 if (flushDelay == -1) {
283 try {
284 // flush delay >= 5, default 60
285 flushDelay = Math.max(5, Integer.parseInt(System.getProperty("java.util.prefs.flushDelay", "60")));
286 } catch (NumberFormatException e) {
287 flushDelay = 60;
288 }
289 }
295 private static synchronized void initSyncTimerIfNeeded()
296 {
297 // syncInterval: -1 is uninitialized, other negative is off,
298 // positive is seconds between syncs (min 5).
299
300 if (syncInterval == -1) {
301 try {
302 syncInterval = Integer.parseInt(System.getProperty("java.util.prefs.syncInterval", "-2"));
303 if (syncInterval >= 0) {
304 // minimum of 5 seconds
305 syncInterval = Math.max(5, syncInterval);
306 } else {
307 syncInterval = -2; // default off
308 }
309 } catch (NumberFormatException e) {
310 syncInterval = -2; // bad property value - default off
311 }
312
313 if (syncInterval > 0) {
314 timer().schedule(new TimerTask() {
315 @Override
316 public void run() {
317 MacOSXPreferencesFile.syncWorld();}
318 }, syncInterval * 1000, syncInterval * 1000);
319 } else {
320 // syncInterval property not set. No sync timer ever.
321 }
322 }
323 }
324
325 // Return the timer used for flush and sync, creating it if necessary.
326 private static synchronized Timer timer()
327 {
328 if (timer == null) {
329 timer = new Timer(true); // daemon
330 Thread flushThread = new Thread() {
331 @Override
332 public void run() {
333 flushWorld();
334 }
335 };
336 /* Set context class loader to null in order to avoid
337 * keeping a strong reference to an application classloader.
338 */
339 flushThread.setContextClassLoader(null);
340 Runtime.getRuntime().addShutdownHook(flushThread);
341 }
342 return timer;
343 }
344
345
346 // Node manipulation
347 boolean addNode(String path)
348 {
349 synchronized(MacOSXPreferencesFile.class) {
350 markChanged();
351 return addNode(path, appName, user, host);
|