Frames | No Frames |
1: /* FileBasedPreferences.java -- File-based preference implementation 2: Copyright (C) 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package gnu.java.util.prefs; 40: 41: import gnu.classpath.SystemProperties; 42: 43: import java.io.File; 44: import java.io.FileInputStream; 45: import java.io.FileOutputStream; 46: import java.io.FilenameFilter; 47: import java.io.IOException; 48: import java.nio.channels.FileChannel; 49: import java.nio.channels.FileLock; 50: import java.util.Properties; 51: import java.util.prefs.AbstractPreferences; 52: import java.util.prefs.BackingStoreException; 53: 54: /** 55: * This is a simple file-based preference implementation which writes 56: * the preferences as properties files. A node is represented as a directory 57: * beneath the user's home directory. The preferences for the node are 58: * stored in a single properties file in that directory. Sub-nodes are 59: * stored in subdirectories. This implementation uses file locking to 60: * mediate access to the properties files. 61: */ 62: public class FileBasedPreferences 63: extends AbstractPreferences 64: { 65: /** 66: * Name of the property file storing the data in a given directory. 67: */ 68: private static final String DATA_FILE = "data.properties"; 69: 70: /** 71: * The directory corresponding to this preference node. 72: */ 73: private File directory; 74: 75: /** 76: * The file holding the data for this node. 77: */ 78: private File dataFile; 79: 80: /** 81: * The data in this node. 82: */ 83: private Properties properties; 84: 85: /** 86: * Create the root node for the file-based preferences. 87: */ 88: FileBasedPreferences() 89: { 90: super(null, ""); 91: String home = SystemProperties.getProperty("user.home"); 92: this.directory = new File(new File(home, ".classpath"), "userPrefs"); 93: this.dataFile = new File(this.directory, DATA_FILE); 94: load(); 95: } 96: 97: /** 98: * Create a new file-based preference object with the given parent 99: * and the given name. 100: * @param parent the parent 101: * @param name the name of this node 102: */ 103: FileBasedPreferences(FileBasedPreferences parent, String name) 104: { 105: super(parent, name); 106: this.directory = new File(parent.directory, name); 107: this.dataFile = new File(this.directory, DATA_FILE); 108: load(); 109: } 110: 111: private void load() 112: { 113: this.properties = new Properties(); 114: FileInputStream fis = null; 115: FileLock lock = null; 116: try 117: { 118: fis = new FileInputStream(this.dataFile); 119: FileChannel channel = fis.getChannel(); 120: lock = channel.lock(0, Long.MAX_VALUE, true); 121: this.properties.load(fis); 122: // We release the lock and close the stream in the 'finally' 123: // clause. 124: } 125: catch (IOException _) 126: { 127: // We don't mind; this means we're making a new node. 128: newNode = true; 129: } 130: finally 131: { 132: try 133: { 134: // Release the lock and close the stream. 135: if (lock != null) 136: lock.release(); 137: } 138: catch (IOException ignore) 139: { 140: // Ignore. 141: } 142: try 143: { 144: // Close the stream. 145: if (fis != null) 146: fis.close(); 147: } 148: catch (IOException ignore) 149: { 150: // Ignore. 151: } 152: } 153: } 154: 155: public boolean isUserNode() 156: { 157: // For now file preferences are always user nodes. 158: return true; 159: } 160: 161: protected String[] childrenNamesSpi() throws BackingStoreException 162: { 163: // FIXME: security manager. 164: String[] result = directory.list(new FilenameFilter() 165: { 166: public boolean accept(File dir, String name) 167: { 168: return new File(dir, name).isDirectory(); 169: } 170: }); 171: if (result == null) 172: result = new String[0]; 173: return result; 174: } 175: 176: protected AbstractPreferences childSpi(String name) 177: { 178: return new FileBasedPreferences(this, name); 179: } 180: 181: protected String[] keysSpi() throws BackingStoreException 182: { 183: return (String[]) properties.keySet().toArray(new String[0]); 184: } 185: 186: protected String getSpi(String key) 187: { 188: return properties.getProperty(key); 189: } 190: 191: protected void putSpi(String key, String value) 192: { 193: properties.put(key, value); 194: } 195: 196: protected void removeSpi(String key) 197: { 198: properties.remove(key); 199: } 200: 201: protected void flushSpi() throws BackingStoreException 202: { 203: // FIXME: security manager. 204: try 205: { 206: if (isRemoved()) 207: { 208: // Delete the underlying file. 209: // FIXME: ideally we would also delete the directory 210: // if it had no subdirectories. This doesn't matter 211: // much though. 212: // FIXME: there's a strange race here if a different VM is 213: // simultaneously updating this node. 214: dataFile.delete(); 215: } 216: else 217: { 218: // Write the underlying file. 219: directory.mkdirs(); 220: 221: FileOutputStream fos = null; 222: FileLock lock = null; 223: try 224: { 225: // Note that we let IOExceptions from the try clause 226: // propagate to the outer 'try'. 227: fos = new FileOutputStream(dataFile); 228: FileChannel channel = fos.getChannel(); 229: lock = channel.lock(); 230: properties.store(fos, "created by GNU Classpath FileBasedPreferences"); 231: // Lock is released and file closed in the finally clause. 232: } 233: finally 234: { 235: try 236: { 237: if (lock != null) 238: lock.release(); 239: } 240: catch (IOException _) 241: { 242: // Ignore. 243: } 244: try 245: { 246: if (fos != null) 247: fos.close(); 248: } 249: catch (IOException _) 250: { 251: // Ignore. 252: } 253: } 254: } 255: } 256: catch (IOException ioe) 257: { 258: throw new BackingStoreException(ioe); 259: } 260: } 261: 262: protected void syncSpi() throws BackingStoreException 263: { 264: // FIXME: we ought to synchronize but instead we merely flush. 265: flushSpi(); 266: } 267: 268: protected void removeNodeSpi() throws BackingStoreException 269: { 270: // We can simply delegate. 271: flushSpi(); 272: } 273: }