Index: components/prefs/src/java/org/apache/jetspeed/prefs/impl/PreferencesImpl.java =================================================================== --- components/prefs/src/java/org/apache/jetspeed/prefs/impl/PreferencesImpl.java (revision 801496) +++ components/prefs/src/java/org/apache/jetspeed/prefs/impl/PreferencesImpl.java (working copy) @@ -16,16 +16,16 @@ */ package org.apache.jetspeed.prefs.impl; +import java.io.OutputStream; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; -import java.util.prefs.AbstractPreferences; -import java.util.prefs.BackingStoreException; +import java.util.prefs.NodeChangeListener; import java.util.prefs.Preferences; +import java.util.prefs.PreferenceChangeListener; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.codec.binary.Base64; import org.apache.jetspeed.prefs.FailedToCreateNodeException; import org.apache.jetspeed.prefs.NodeAlreadyExistsException; import org.apache.jetspeed.prefs.NodeDoesNotExistException; @@ -34,62 +34,181 @@ import org.apache.jetspeed.prefs.om.impl.PropertyImpl; /** - *

- * S {@link Preferences}implementation relying on Jetspeed OJB based - * persistence plugin. - *

+ * PreferencesImpl * + * transient non-caching implementation relying on Jetspeed OJB based + * persistence plugin + * * @author David Le Strat - * @author Ate Douma + * @author Ate Douma + * @author Randy Watler */ -public class PreferencesImpl extends AbstractPreferences +public class PreferencesImpl extends Preferences { - /** User Preferences node type. */ public static final int USER_NODE_TYPE = 0; /** System Preferences node type. */ public static final int SYSTEM_NODE_TYPE = 1; - /** Logger. */ - private static final Log log = LogFactory.getLog(PreferencesImpl.class); - - PreferencesProviderWrapper ppw; - - String nodeName; + private String path; + private PreferencesProviderWrapper ppw; + private int type; + private boolean removed; - int nodeType; - /** - *

- * Constructs a root node in the underlying datastore if they have not yet - * been created. - *

- *

- * Logs a warning if the underlying datastore is unavailable. - *

+ * Construct Java preferences transient view of persistent Jetspeed + * preferences that will not be cached. * - * @param parent The parent object. - * @param nodeName The node name. - * @param nodeType The node type. + * @param ppw preferences provider + * @param path absolute path + * @param type user or system type */ - PreferencesImpl(PreferencesImpl parent, PreferencesProviderWrapper ppw, String nodeName, int nodeType) throws IllegalStateException + public PreferencesImpl(PreferencesProviderWrapper ppw, String path, int type) { - super(parent, nodeName); this.ppw = ppw; - this.nodeName = nodeName; - this.nodeType = nodeType; - // aggressively fetch/create node + this.path = path; + this.type = type; + this.removed = false; + // get/create persistent node getNode(); } - /** - * @see java.util.prefs.Preferences#childrenNamesSpi() + /* (non-Javadoc) + * @see java.util.prefs.Preferences#put(java.lang.String, java.lang.String) */ - public String[] childrenNamesSpi() throws BackingStoreException + public void put(String key, String value) { - Collection nodes = ppw.provider().getChildren(getNode()); - + // check removed state + if (removed) + { + throw new IllegalStateException("Preferences node removed"); + } + // get persistent node and properties + Node node = getNode(); + Collection properties = node.getNodeProperties(); + // if the property exists, update its value. + boolean propFound = false; + for (Iterator i = properties.iterator(); i.hasNext();) + { + Property curProp = (Property) i.next(); + if ((null != curProp) && (null != curProp.getPropertyName()) && curProp.getPropertyName().equals(key)) + { + propFound = true; + curProp.setPropertyValue(value); + curProp.setModifiedDate(new Timestamp(System.currentTimeMillis())); + break; + } + } + // add new property value + if (!propFound) + { + properties.add(new PropertyImpl(node.getNodeId(), key, value)); + } + // update node + ppw.provider().storeNode(node); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#get(java.lang.String, java.lang.String) + */ + public String get(String key, String def) + { + // check removed state + if (removed) + { + throw new IllegalStateException("Preferences node removed"); + } + // get persistent node and properties + Node node = getNode(); + Collection properties = node.getNodeProperties(); + // return value + for (Iterator i = properties.iterator(); i.hasNext();) + { + Property curProp = (Property) i.next(); + if ((null != curProp) && (null != curProp.getPropertyName()) && (curProp.getPropertyName().equals(key))) + { + return curProp.getPropertyValue(); + } + } + return def; + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#remove(java.lang.String) + */ + public void remove(String key) + { + // check removed state + if (removed) + { + throw new IllegalStateException("Preferences node removed"); + } + // get persistent node and properties + Node node = getNode(); + Collection properties = node.getNodeProperties(); + // remove property if found + boolean removed = false; + for (Iterator i = properties.iterator(); i.hasNext();) + { + Property curProp = (Property) i.next(); + if ((curProp.getPropertyName().equals(key))) + { + i.remove(); + removed = true; + break; + } + } + // update node if removed + if (removed) + { + ppw.provider().storeNode(node); + } + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#keys() + */ + public String[] keys() + { + // check removed state + if (removed) + { + throw new IllegalStateException("Preferences node removed"); + } + // get persistent node and properties + Node node = getNode(); + Collection properties = node.getNodeProperties(); + // extract property names from nod + ArrayList propertyNames = new ArrayList(); + if ((null != properties) && properties.size() > 0) + { + for (Iterator j = properties.iterator(); j.hasNext();) + { + Property curprop = (Property) j.next(); + if ((null != curprop) && (null != curprop.getPropertyName()) && !propertyNames.contains(curprop.getPropertyName())) + { + propertyNames.add(curprop.getPropertyName()); + } + } + } + return (String[]) propertyNames.toArray(new String[propertyNames.size()]); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#childrenNames() + */ + public String[] childrenNames() + { + // check removed state + if (removed) + { + throw new IllegalStateException("Preferences node removed"); + } + // get persistent node and child nodes + Node node = getNode(); + Collection nodes = ppw.provider().getChildren(node); + // get child node names if (null != nodes) { ArrayList childrenNames = new ArrayList(nodes.size()); @@ -107,214 +226,507 @@ return new String[0]; } } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#removeNode() + */ + public void removeNode() + { + // check removed state + if (removed) + { + throw new IllegalStateException("Preferences node removed"); + } + // check for removal of root node + if (path.equals("/")) + { + throw new UnsupportedOperationException("Root preferences node cannot be removed"); + } + // get persistent node and parent + Node node = getNode(path, type, false); + if (node != null) + { + Node parentNode = getNode(parentPath(path), type, false); + if (parentNode != null) + { + // remove persistent node and children + removeNodeAndChildren(parentNode, node); + } + } + // flag preference as removed + removed = true; + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#nodeExists(java.lang.String) + */ + public boolean nodeExists(String path) + { + // check removed state + if (removed) + { + throw new IllegalStateException("Preferences node removed"); + } + // construct absolute path if necessary + if (path.charAt(0) != '/') + { + if (!this.path.equals("/")) + { + path = this.path+"/"+path; + } + else + { + path = "/"+path; + } + } + // check to see if persistent node exists + return (getNode(path, type, false) != null); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#sync() + */ + public void sync() + { + } - /** - * @see java.util.prefs.Preferences#childSpi(java.lang.String) + /* (non-Javadoc) + * @see java.util.prefs.Preferences#flush() */ - public AbstractPreferences childSpi(String name) + public void flush() { - return new PreferencesImpl(this, ppw, name, nodeType); } - /** - * @see java.util.prefs.Preferences#flushSpi() + /* (non-Javadoc) + * @see java.util.prefs.Preferences#parent() */ - public void flushSpi() throws BackingStoreException + public Preferences parent() { + // check removed state + if (removed) + { + throw new IllegalStateException("Preferences node removed"); + } + // construct parent preference + if (!path.equals("/")) + { + return new PreferencesImpl(ppw, parentPath(path), type); + } + return null; } - /** - * @see java.util.prefs.Preferences#getSpi(java.lang.String) + /* (non-Javadoc) + * @see java.util.prefs.Preferences#node(java.lang.String) */ - public String getSpi(String key) + public Preferences node(String path) { - String value = null; - Collection properties = getNode().getNodeProperties(); - for (Iterator i = properties.iterator(); i.hasNext();) + // check removed state + if (removed) { - Property curProp = (Property) i.next(); - if ((null != curProp) && (null != curProp.getPropertyName()) && (curProp.getPropertyName().equals(key))) + throw new IllegalStateException("Preferences node removed"); + } + // construct absolute path if necessary + if (path.charAt(0) != '/') + { + if (!this.path.equals("/")) { - value = curProp.getPropertyValue(); + path = this.path+"/"+path; } + else + { + path = "/"+path; + } } - return value; + // return new preference + return new PreferencesImpl(ppw, path, type); } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#name() + */ + public String name() + { + // get name from path + return pathName(path); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#absolutePath() + */ + public String absolutePath() + { + // return path + return path; + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#isUserNode() + */ + public boolean isUserNode() + { + // test type + return (type == USER_NODE_TYPE); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#clear() + */ + public void clear() + { + // remove all keys + String[] keys = keys(); + for (int i = 0; (i < keys.length); i++) + { + remove(keys[i]); + } + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#addPreferenceChangeListener(java.util.prefs.PreferenceChangeListener) + */ + public void addPreferenceChangeListener(PreferenceChangeListener pcl) + { + // change listeners not supported + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#removePreferenceChangeListener(java.util.prefs.PreferenceChangeListener) + */ + public void removePreferenceChangeListener(PreferenceChangeListener pcl) + { + // change listeners not supported + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#addNodeChangeListener(java.util.prefs.NodeChangeListener) + */ + public void addNodeChangeListener(NodeChangeListener ncl) + { + // change listeners not supported + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#removeNodeChangeListener(java.util.prefs.NodeChangeListener) + */ + public void removeNodeChangeListener(NodeChangeListener ncl) + { + // change listeners not supported + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#putInt(java.lang.String, int) + */ + public void putInt(String key, int value) + { + put(key, Integer.toString(value)); + } - /** - * @see java.util.prefs.Preferences#keysSpi() + /* (non-Javadoc) + * @see java.util.prefs.Preferences#getInt(java.lang.String, int) */ - public String[] keysSpi() + public int getInt(String key, int def) { - ArrayList propertyNames = new ArrayList(); - - Collection propCol = getNode().getNodeProperties(); - if ((null != propCol) && propCol.size() > 0) + try { - for (Iterator j = propCol.iterator(); j.hasNext();) + String value = get(key, null); + if (value != null) { - Property curprop = (Property) j.next(); - if ((null != curprop) && (null != curprop.getPropertyName()) - && !propertyNames.contains(curprop.getPropertyName())) - { - propertyNames.add(curprop.getPropertyName()); - } + return Integer.parseInt(value); } } + catch (NumberFormatException nfe) + { + } + return def; + } - return (String[]) propertyNames.toArray(new String[propertyNames.size()]); + /* (non-Javadoc) + * @see java.util.prefs.Preferences#putLong(java.lang.String, long) + */ + public void putLong(String key, long value) + { + put(key, Long.toString(value)); } - /** - * @see java.util.prefs.Preferences#putSpi(java.lang.String, - * java.lang.String) + /* (non-Javadoc) + * @see java.util.prefs.Preferences#getLong(java.lang.String, long) */ - public void putSpi(String key, String value) + public long getLong(String key, long def) { - Node node = getNode(); - Collection properties = node.getNodeProperties(); - if (null == properties) + try { - log.error("Could not retrieve node property: [key: " + key + ", value:" + value + "]"); - return; - } - - // If the property exists, update its value. - boolean propFound = false; - for (Iterator i = properties.iterator(); i.hasNext();) - { - Property curProp = (Property) i.next(); - if ((null != curProp) && (null != curProp.getPropertyName()) && curProp.getPropertyName().equals(key)) + String value = get(key, null); + if (value != null) { - propFound = true; - curProp.setPropertyValue(value); - curProp.setModifiedDate(new Timestamp(System.currentTimeMillis())); - if (log.isDebugEnabled()) - { - log.debug("Update existing property: " + curProp.toString()); - } - // Property found, we break. - break; + return Long.parseLong(value); } } - if (!propFound) + catch (NumberFormatException nfe) { - properties.add(new PropertyImpl(node.getNodeId(), key, value)); } + return def; + } - ppw.provider().storeNode(node); + /* (non-Javadoc) + * @see java.util.prefs.Preferences#putBoolean(java.lang.String, boolean) + */ + public void putBoolean(String key, boolean value) + { + put(key, String.valueOf(value)); + } - // mark stored node as old - newNode = false; + /* (non-Javadoc) + * @see java.util.prefs.Preferences#getBoolean(java.lang.String, boolean) + */ + public boolean getBoolean(String key, boolean def) + { + String value = get(key, null); + if (value != null) + { + if (value.equalsIgnoreCase("true")) + { + return true; + } + else if (value.equalsIgnoreCase("false")) + { + return false; + } + } + return def; } - /** - * @see java.util.prefs.Preferences#removeNodeSpi() + /* (non-Javadoc) + * @see java.util.prefs.Preferences#putFloat(java.lang.String, float) */ - public void removeNodeSpi() throws BackingStoreException + public void putFloat(String key, float value) { - // remove node from db - Node parentNode = null; - Preferences parent = parent(); - if (parent != null && parent instanceof PreferencesImpl) + put(key, Float.toString(value)); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#getFloat(java.lang.String, float) + */ + public float getFloat(String key, float def) + { + try { - parentNode = ((PreferencesImpl) parent).getNode(); + String value = get(key, null); + if (value != null) + { + return Float.parseFloat(value); + } } - ppw.provider().removeNode(parentNode, getNode()); + catch (NumberFormatException nfe) + { + } + return def; + } - // mark removed node as new - newNode = true; + /* (non-Javadoc) + * @see java.util.prefs.Preferences#putDouble(java.lang.String, double) + */ + public void putDouble(String key, double value) + { + put(key, Double.toString(value)); } - /** - * @see java.util.prefs.Preferences#removeSpi(java.lang.String) + /* (non-Javadoc) + * @see java.util.prefs.Preferences#getDouble(java.lang.String, double) */ - public void removeSpi(String key) + public double getDouble(String key, double def) { - boolean removed = false; - Node node = getNode(); - Collection properties = node.getNodeProperties(); - for (Iterator i = properties.iterator(); i.hasNext();) + try { - Property curProp = (Property) i.next(); - - if ((curProp.getPropertyName().equals(key))) + String value = get(key, null); + if (value != null) { - i.remove(); - removed = true; + return Double.parseDouble(value); } } - if (removed) + catch (NumberFormatException nfe) { - ppw.provider().storeNode(node); + } + return def; + } - // mark stored node as old - newNode = false; + /* (non-Javadoc) + * @see java.util.prefs.Preferences#putByteArray(java.lang.String, byte[]) + */ + public void putByteArray(String key, byte[] value) + { + put(key, new String(Base64.encodeBase64(value))); + } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#getByteArray(java.lang.String, byte[]) + */ + public byte[] getByteArray(String key, byte[] def) + { + String value = get(key, null); + if (value != null) + { + return Base64.decodeBase64(value.getBytes()); } + return def; } - /** - * @see java.util.prefs.Preferences#syncSpi() + /* (non-Javadoc) + * @see java.util.prefs.Preferences#exportNode(java.io.OutputStream) */ - public void syncSpi() throws BackingStoreException + public void exportNode(OutputStream os) { - flushSpi(); + // export not supported + throw new UnsupportedOperationException(); } + + /* (non-Javadoc) + * @see java.util.prefs.Preferences#exportSubtree(java.io.OutputStream) + */ + public void exportSubtree(OutputStream os) + { + // export not supported + throw new UnsupportedOperationException(); + } + /* (non-Javadoc) + * @see java.util.prefs.Preferences#toString() + */ + public String toString() + { + // return string representation of preference node + return (isUserNode() ? "User" : "System")+" Preference Node: "+path; + } + /** + * Get preferences persistence provider. * - *

- * getNode - *

+ * @return preferences provider + */ + public PreferencesProviderWrapper getProvider() + { + return ppw; + } + + /** + * Get or create persistent preference node for this preferences node. * - * @return + * @return persistent preference node */ - public Node getNode() + private Node getNode() { - Node node; + return getNode(path, type, true); + } + + /** + * Get or create persistent preference node. + * + * @param path absolute path + * @param type user or system type + * @param create flag to enable node creation on access + * @return persistent preference node + */ + private Node getNode(String path, int type, boolean create) + { + // access node + Node node = null; try { - node = ppw.provider().getNode(absolutePath(), nodeType); - newNode = false; + node = ppw.provider().getNode(path, type); } catch (NodeDoesNotExistException e1) { - try + // create if required and does not exist + if (create) { - Preferences parent = parent(); - if ((parent != null) && (parent instanceof PreferencesImpl)) + try { - node = ppw.provider().createNode(((PreferencesImpl)parent).getNode(), nodeName, nodeType, absolutePath()); + // create node and set new + String parentPath = parentPath(path); + String name = pathName(path); + if (parentPath.length() > 0) + { + node = ppw.provider().createNode(getNode(parentPath, type, true), name, type, path); + } + else + { + node = ppw.provider().createNode(null, name, type, path); + } } - else + catch (FailedToCreateNodeException e) { - node = ppw.provider().createNode(null, nodeName, nodeType, absolutePath()); + IllegalStateException ise = new IllegalStateException("Failed to create new Preferences of type " + type + " for path " + path); + ise.initCause(e); + throw ise; } - newNode = true; - } - catch (FailedToCreateNodeException e) - { - IllegalStateException ise = new IllegalStateException("Failed to create new Preferences of type " + nodeType + " for path " + absolutePath()); - ise.initCause(e); - throw ise; - } - catch (NodeAlreadyExistsException e) - { - try + catch (NodeAlreadyExistsException e) { - node = ppw.provider().getNode(absolutePath(), nodeType); - newNode = false; + // retry to create node in case just created + try + { + node = ppw.provider().getNode(path, type); + } + catch (NodeDoesNotExistException e2) + { + // If we get this at this point something is very wrong + IllegalStateException ise = new IllegalStateException("Unable to create node for Preferences of type " + type + " for path " + path + ". " + + "If you see this exception at this, it more than likely means that the Preferences backing store is corrupt."); + ise.initCause(e2); + throw ise; + } } - catch (NodeDoesNotExistException e2) - { - // If we get this at this point something is very wrong - IllegalStateException ise = new IllegalStateException("Unable to create node for Preferences of type " + nodeType + " for path " + absolutePath() + ". " + - "If you see this exception at this, it more than likely means that the Preferences backing store is corrupt."); - ise.initCause(e2); - throw ise; - } } } return node; } + + /** + * Recursively remove node and all child nodes. + * + * @param parentNode parent of node to remove + * @param node preference node to remove + */ + private void removeNodeAndChildren(Node parentNode, Node node) + { + Collection nodes = ppw.provider().getChildren(node); + if (null != nodes) + { + for (Iterator i = nodes.iterator(); i.hasNext();) + { + Node childNode = (Node) i.next(); + removeNodeAndChildren(node, childNode); + } + } + ppw.provider().removeNode(parentNode, node); + } + + /** + * Utility to compute parent path from node path. + * + * @param path absolute node path + * @return parent node path + */ + private static String parentPath(String path) + { + int lastIndex = path.lastIndexOf('/'); + if (lastIndex > 0) + { + return path.substring(0, lastIndex); + } + return "/"; + } + + /** + * Utility to extract node name from node path. + * + * @param path absolute node path + * @return node name + */ + private static String pathName(String path) + { + return path.substring(path.lastIndexOf('/')+1); + } } \ No newline at end of file Index: components/prefs/src/java/org/apache/jetspeed/prefs/impl/PreferencesFactoryImpl.java =================================================================== --- components/prefs/src/java/org/apache/jetspeed/prefs/impl/PreferencesFactoryImpl.java (revision 801357) +++ components/prefs/src/java/org/apache/jetspeed/prefs/impl/PreferencesFactoryImpl.java (working copy) @@ -87,8 +87,8 @@ // which can be disposed at once for all PreferencesProviderWrapper ppw = new PreferencesProviderWrapper(preferencesProvider); preferencesProvider = null; - userRoot = new PreferencesImpl(null, ppw, "", PreferencesImpl.USER_NODE_TYPE); - systemRoot = new PreferencesImpl(null, ppw, "", PreferencesImpl.SYSTEM_NODE_TYPE); + userRoot = new PreferencesImpl(ppw, "/", PreferencesImpl.USER_NODE_TYPE); + systemRoot = new PreferencesImpl(ppw, "/", PreferencesImpl.SYSTEM_NODE_TYPE); // set/update the Java Preferences userRoot and systeRoot PreferencesRootWrapper instances ((Observer)Preferences.userRoot()).update(null, userRoot); ((Observer)Preferences.systemRoot()).update(null, systemRoot); @@ -103,7 +103,7 @@ { ((Observer) Preferences.userRoot()).update(null, null); ((Observer) Preferences.systemRoot()).update(null, null); - userRoot.ppw.dispose(); + userRoot.getProvider().dispose(); userRoot = null; systemRoot = null; } Index: components/prefs/pom.xml =================================================================== --- components/prefs/pom.xml (revision 801357) +++ components/prefs/pom.xml (working copy) @@ -50,6 +50,10 @@ ${pom.groupId} jetspeed-rdbms + + commons-codec + commons-codec + ${pom.groupId} jetspeed-commons