Frames | No Frames |
1: /* GConfBasedPreferences.java -- GConf based Preferences 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: package gnu.java.util.prefs; 39: 40: import gnu.java.util.prefs.gconf.GConfNativePeer; 41: 42: import java.security.Permission; 43: 44: import java.util.List; 45: import java.util.prefs.AbstractPreferences; 46: import java.util.prefs.BackingStoreException; 47: 48: /** 49: * This is a GConf based preference implementation which writes the preferences 50: * as GConf key-value pairs. System Root is defined to be the 51: * <code>"/system"</code> directory of GConf for the current user, while User 52: * Root is <code>"/apps/java"</code>. These defaults can be modified by 53: * defining two system properties:<br /> 54: * <br /> 55: * User Root:<br /> 56: * <br /> 57: * 58: * <pre> 59: * gnu.java.util.prefs.gconf.user_root 60: * </pre> 61: * 62: * <br /> 63: * <br /> 64: * and System Root:<br /> 65: * <br /> 66: * 67: * <pre> 68: * gnu.java.util.prefs.gconf.system_root 69: * </pre> 70: * 71: * <br /> 72: * 73: * @author Mario Torre <neugens@limasoftware.net> 74: */ 75: public class GConfBasedPreferences 76: extends AbstractPreferences 77: { 78: /** Get access to Runtime permission */ 79: private static final Permission PERMISSION 80: = new RuntimePermission("preferences"); 81: 82: /** CGonf client backend */ 83: private static GConfNativePeer backend = new GConfNativePeer(); 84: 85: /** Default user root path */ 86: private static final String DEFAULT_USER_ROOT = "/apps/classpath"; 87: 88: /** Default system root path */ 89: private static final String DEFAULT_SYSTEM_ROOT = "/system"; 90: 91: /** current node full path */ 92: private String node = ""; 93: 94: /** True if this is a preference node in the user tree, false otherwise. */ 95: private final boolean isUser; 96: 97: /** 98: * Creates a preference root user node. 99: */ 100: public GConfBasedPreferences() 101: { 102: this(true); 103: } 104: 105: /** 106: * Creates a preference root node. When <code>isUser</code> is true it will 107: * be user node otherwise it will be a system node. 108: */ 109: public GConfBasedPreferences(boolean isUser) 110: { 111: this(null, "", isUser); 112: } 113: 114: /** 115: * Creates a new preference node given a parent node and a name, which has to 116: * be relative to its parent. When <code>isUser</code> is true it will be user 117: * node otherwise it will be a system node. 118: * 119: * @param parent The parent node of this newly created node. 120: * @param name A name relative to the parent node. 121: * @param isUser Set to <code>true</code> initializes this node to be 122: * a user node, <code>false</code> initialize it to be a system node. 123: */ 124: public GConfBasedPreferences(AbstractPreferences parent, String name, 125: boolean isUser) 126: { 127: super(parent, name); 128: this.isUser = isUser; 129: 130: // stores the fully qualified name of this node 131: String absolutePath = this.absolutePath(); 132: if (absolutePath != null && absolutePath.endsWith("/")) 133: { 134: absolutePath = absolutePath.substring(0, absolutePath.length() - 1); 135: } 136: 137: // strip invalid characters 138: // please, note that all names are unescaped into the native peer 139: int index = absolutePath.lastIndexOf('/'); 140: if (index > -1) 141: { 142: absolutePath = absolutePath.substring(0, index + 1); 143: absolutePath = absolutePath + GConfNativePeer.escapeString(name); 144: } 145: 146: this.node = this.getRealRoot(isUser) + absolutePath; 147: 148: boolean nodeExist = backend.nodeExist(this.node); 149: 150: this.newNode = !nodeExist; 151: } 152: 153: /** 154: * Returns a child node with the given name. 155: * If the child node does not exists, it will be created. 156: * 157: * @param name The name of the requested node. 158: * @return A new reference to the node, creating the node if it is necessary. 159: */ 160: protected AbstractPreferences childSpi(String name) 161: { 162: // we don't check anything here, if the node is a new node this will be 163: // detected in the constructor, so we simply return a new reference to 164: // the requested node. 165: 166: GConfBasedPreferences preferenceNode 167: = new GConfBasedPreferences(this, name, this.isUser); 168: 169: return preferenceNode; 170: } 171: 172: /** 173: * Returns an array of names of the children of this preference node. 174: * If the current node does not have children, the returned array will be 175: * of <code>size</code> 0 (that is, not <code>null</code>). 176: * 177: * @return A <code>String</code> array of names of children of the current 178: * node. 179: * @throws BackingStoreException if this operation cannot be completed. 180: */ 181: protected String[] childrenNamesSpi() throws BackingStoreException 182: { 183: List<String> nodeList = backend.getChildrenNodes(this.node); 184: String[] nodes = new String[nodeList.size()]; 185: nodeList.toArray(nodes); 186: 187: return nodes; 188: } 189: 190: /** 191: * Suggest a flush to the backend. Actually, this is only a suggestion as 192: * GConf handles this for us asynchronously. More over, both sync and flush 193: * have the same meaning in this class, so calling sync has exactly the same 194: * effect. 195: * 196: * @see #sync 197: * @throws BackingStoreException if this operation cannot be completed. 198: */ 199: public void flush() throws BackingStoreException 200: { 201: backend.suggestSync(); 202: } 203: 204: /** 205: * Request a flush. 206: * 207: * @see #flush 208: * @throws BackingStoreException if this operation cannot be completed. 209: */ 210: protected void flushSpi() throws BackingStoreException 211: { 212: this.flush(); 213: } 214: 215: /** 216: * Returns all of the key in this preference node. 217: * If the current node does not have preferences, the returned array will be 218: * of size zero. 219: * 220: * @return A <code>String</code> array of keys stored under the current 221: * node. 222: * @throws BackingStoreException if this operation cannot be completed. 223: */ 224: protected String[] keysSpi() throws BackingStoreException 225: { 226: List<String> keyList = backend.getKeys(this.node); 227: String[] keys = new String[keyList.size()]; 228: keyList.toArray(keys); 229: 230: return keys; 231: } 232: 233: /** 234: * Does a recursive postorder traversal of the preference tree, starting from 235: * the given directory invalidating every preference found in the node. 236: * 237: * @param directory The name of the starting directory (node) 238: */ 239: private void postorderRemove(String directory) 240: { 241: try 242: { 243: // gets the listing of directories in this node 244: List<String> dirs = backend.getChildrenNodes(directory); 245: 246: if (dirs.size() != 0) 247: { 248: for (String currentDir : dirs) 249: { 250: // recursive search inside this directory 251: postorderRemove(currentDir); 252: } 253: } 254: 255: // remove all the keys associated to this directory 256: List<String> entries = backend.getKeys(directory); 257: 258: if (entries.size() != 0) 259: { 260: for (String key : entries) 261: { 262: this.removeSpi(key); 263: } 264: } 265: } 266: catch (BackingStoreException ex) 267: { 268: /* ignore */ 269: } 270: } 271: 272: /** 273: * Stores the given key-value pair into this preference node. 274: * 275: * @param key The key of this preference. 276: * @param value The value of this preference. 277: */ 278: protected void putSpi(String key, String value) 279: { 280: backend.setString(this.getGConfKey(key), value); 281: } 282: 283: /** 284: * Removes this preference node, including all its children. 285: * Also removes the preferences associated. 286: */ 287: protected void removeNodeSpi() throws BackingStoreException 288: { 289: this.postorderRemove(this.node); 290: this.flush(); 291: } 292: 293: /** 294: * Removes the given key from this preference node. 295: * If the key does not exist, no operation is performed. 296: * 297: * @param key The key to remove. 298: */ 299: protected void removeSpi(String key) 300: { 301: backend.unset(this.getGConfKey(key)); 302: } 303: 304: /** 305: * Suggest a sync to the backend. Actually, this is only a suggestion as GConf 306: * handles this for us asynchronously. More over, both sync and flush have the 307: * same meaning in this class, so calling flush has exactly the same effect. 308: * 309: * @see #flush 310: * @throws BackingStoreException if this operation cannot be completed due to 311: * a failure in the backing store, or inability to communicate with 312: * it. 313: */ 314: public void sync() throws BackingStoreException 315: { 316: this.flush(); 317: } 318: 319: /** 320: * Request a sync. 321: * 322: * @see #sync 323: * @throws BackingStoreException if this operation cannot be completed due to 324: * a failure in the backing store, or inability to communicate with 325: * it. 326: */ 327: protected void syncSpi() throws BackingStoreException 328: { 329: this.sync(); 330: } 331: 332: /** 333: * Returns the value of the given key. 334: * If the keys does not have a value, or there is an error in the backing 335: * store, <code>null</code> is returned instead. 336: * 337: * @param key The key to retrieve. 338: * @return The value associated with the given key. 339: */ 340: protected String getSpi(String key) 341: { 342: return backend.getKey(this.getGConfKey(key)); 343: } 344: 345: /** 346: * Returns <code>true</code> if this preference node is a user node, 347: * <code>false</code> if is a system preference node. 348: * 349: * @return <code>true</code> if this preference node is a user node, 350: * <code>false</code> if is a system preference node. 351: */ 352: public boolean isUserNode() 353: { 354: return this.isUser; 355: } 356: 357: /* 358: * PRIVATE METHODS 359: */ 360: 361: /** 362: * Builds a GConf key string suitable for operations on the backend. 363: * 364: * @param key The key to convert into a valid GConf key. 365: * @return A valid Gconf key. 366: */ 367: private String getGConfKey(String key) 368: { 369: String nodeName = ""; 370: 371: // strip key 372: // please, note that all names are unescaped into the native peer 373: key = GConfNativePeer.escapeString(key); 374: 375: if (this.node.endsWith("/")) 376: { 377: nodeName = this.node + key; 378: } 379: else 380: { 381: nodeName = this.node + "/" + key; 382: } 383: 384: return nodeName; 385: } 386: 387: /** 388: * Builds the root node to use for this preference. 389: * 390: * @param isUser Defines if this node is a user (<code>true</code>) or system 391: * (<code>false</code>) node. 392: * @return The real root of this preference tree. 393: */ 394: private String getRealRoot(boolean isUser) 395: { 396: // not sure about this, we should have already these permissions... 397: SecurityManager security = System.getSecurityManager(); 398: 399: if (security != null) 400: { 401: security.checkPermission(PERMISSION); 402: } 403: 404: String root = null; 405: 406: if (isUser) 407: { 408: root = System.getProperty("gnu.java.util.prefs.gconf.user_root", 409: DEFAULT_USER_ROOT); 410: } 411: else 412: { 413: root = System.getProperty("gnu.java.util.prefs.gconf.system_root", 414: DEFAULT_SYSTEM_ROOT); 415: } 416: 417: return root; 418: } 419: }