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