From c0830be33685db33f86af7bf7dd2f8aaee250f57 Mon Sep 17 00:00:00 2001 From: hns Date: Thu, 23 Oct 2003 12:00:35 +0000 Subject: [PATCH] DbMapping: Changed most fields visibility to private DbMapping: Change getPropertyRelation visibility to private, always use getSubnodeRelation from outside instead. Node: Rewrote getProperty(String) to be simpler and easier to understand. furthermore, getProperty(String) now also returns child objects (subnodes) making it unnecessary for calling code to also use getChildElement() or getSubnode() Node: Fixed NullPointerException in getElementName() for transient nodes Node: Use DbMapping.getSubnodeRelation() instead of DbMapping.getPropertyRelation() NodeManager: Fixes in getNode() methods, which now invoke each other depending on key/query type Relation: Removed unused arguments from constructor Relation: Fixed NullPointerException in createOnDemand() HopObject: Do not use getChildElement() in jsFunction_get() (see change in Node.getProperty()) HopObject: Handle case where jsFunction_get() and jsFunction_set() are called with non-strings that should be handled as string, e.g. org.mozilla.javascript.NativeString cosmetic cleanup everywhere (imports, unneeded casts, unused fields etc.) --- src/helma/objectmodel/db/DbMapping.java | 58 +++--- src/helma/objectmodel/db/Node.java | 210 +++++++++++----------- src/helma/objectmodel/db/NodeManager.java | 25 ++- src/helma/objectmodel/db/Relation.java | 13 +- src/helma/scripting/rhino/HopObject.java | 35 ++-- 5 files changed, 168 insertions(+), 173 deletions(-) diff --git a/src/helma/objectmodel/db/DbMapping.java b/src/helma/objectmodel/db/DbMapping.java index 5622741a..74a612a9 100644 --- a/src/helma/objectmodel/db/DbMapping.java +++ b/src/helma/objectmodel/db/DbMapping.java @@ -20,13 +20,9 @@ import helma.framework.core.Application; import helma.framework.core.Prototype; import helma.util.SystemProperties; import helma.util.Updatable; + import java.sql.*; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.StringTokenizer; +import java.util.*; /** * A DbMapping describes how a certain type of Nodes is to mapped to a @@ -35,76 +31,76 @@ import java.util.StringTokenizer; */ public final class DbMapping implements Updatable { // DbMappings belong to an application - Application app; + protected Application app; // prototype name of this mapping private String typename; // properties from where the mapping is read - SystemProperties props; + private SystemProperties props; // name of data dbSource to which this mapping writes - DbSource dbSource; + private DbSource dbSource; // name of datasource - String dbSourceName; + private String dbSourceName; // name of db table - String tableName; + private String tableName; // list of properties to try for parent - ParentInfo[] parentInfo; + private ParentInfo[] parentInfo; // Relations describing subnodes and properties. - Relation subRelation; - Relation propRelation; + protected Relation subRelation; + protected Relation propRelation; // if this defines a subnode mapping with groupby layer, // we need a DbMapping for those groupby nodes - DbMapping groupbyMapping; + private DbMapping groupbyMapping; // Map of property names to Relations objects - HashMap prop2db; + private HashMap prop2db; // Map of db columns to Relations objects. // Case insensitive, keys are stored in upper case so // lookups must do a toUpperCase(). - HashMap db2prop; + private HashMap db2prop; // list of columns to fetch from db - DbColumn[] columns = null; + private DbColumn[] columns = null; // Map of db columns by name - HashMap columnMap; + private HashMap columnMap; // Array of aggressively loaded references - Relation[] joins; + private Relation[] joins; // pre-rendered select statement - String selectString = null; - String insertString = null; - String updateString = null; + private String selectString = null; + private String insertString = null; + private String updateString = null; // db field used as primary key private String idField; // db field used as object name - String nameField; + private String nameField; // db field used to identify name of prototype to use for object instantiation - String protoField; + private String protoField; // name of parent prototype, if any - String extendsProto; + private String extendsProto; // dbmapping of parent prototype, if any - DbMapping parentMapping; + private DbMapping parentMapping; // descriptor for key generation method private String idgen; // remember last key generated for this table - long lastID; + private long lastID; // timestamp of last modification of the mapping (type.properties) // init value is -1 so we know we have to run update once even if @@ -262,7 +258,7 @@ public final class DbMapping implements Updatable { Relation rel = (Relation) prop2db.get(propName.toLowerCase()); if (rel == null) { - rel = new Relation(dbField, propName, this, props); + rel = new Relation(propName, this); } rel.update(dbField, props); @@ -312,7 +308,7 @@ public final class DbMapping implements Updatable { try { // check if subnode relation already exists. If so, reuse it if (subRelation == null) { - subRelation = new Relation(subnodeMapping, "_children", this, props); + subRelation = new Relation("_children", this); } subRelation.update(subnodeMapping, props); @@ -695,7 +691,7 @@ public final class DbMapping implements Updatable { * * @return ... */ - public Relation getPropertyRelation() { + private Relation getPropertyRelation() { if ((propRelation == null) && (parentMapping != null)) { return parentMapping.getPropertyRelation(); } diff --git a/src/helma/objectmodel/db/Node.java b/src/helma/objectmodel/db/Node.java index 0837d40f..f51d1a14 100644 --- a/src/helma/objectmodel/db/Node.java +++ b/src/helma/objectmodel/db/Node.java @@ -17,18 +17,17 @@ package helma.objectmodel.db; import helma.framework.IPathElement; -import helma.objectmodel.*; -import helma.util.*; -import java.io.*; -import java.math.BigDecimal; -import java.sql.*; -import java.util.Date; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; -import java.util.StringTokenizer; -import java.util.Vector; +import helma.objectmodel.ConcurrencyException; +import helma.objectmodel.INode; +import helma.objectmodel.IProperty; +import helma.objectmodel.TransientNode; +import helma.util.EmptyEnumeration; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.*; /** * An implementation of INode that can be stored in the internal database or @@ -182,8 +181,6 @@ public final class Node implements INode, Serializable { // version indicates the serialization version version = in.readShort(); - String rawParentID = null; - if (version < 9) { throw new IOException("Can't read pre 1.3.0 HopObject"); } @@ -418,12 +415,17 @@ public final class Node implements INode, Serializable { public String getElementName() { // if subnodes are also mounted as properties, try to get the "nice" prop value // instead of the id by turning the anonymous flag off. - long lastmod = Math.max(dbmap.getLastTypeChange(), lastmodified); + long lastmod = lastmodified; + + if (dbmap != null) { + lastmod = Math.max(lastmod, dbmap.getLastTypeChange()); + } + if ((parentHandle != null) && (lastNameCheck < lastmod)) { try { Node p = parentHandle.getNode(nmgr); DbMapping parentmap = p.getDbMapping(); - Relation prel = parentmap.getPropertyRelation(); + Relation prel = parentmap.getSubnodeRelation(); if ((prel != null) && prel.hasAccessName()) { String propname = dbmap.columnNameToProperty(prel.accessName); @@ -672,7 +674,7 @@ public final class Node implements INode, Serializable { if (parentmap != null) { // first try to retrieve name via generic property relation of parent - Relation prel = parentmap.getPropertyRelation(); + Relation prel = parentmap.getSubnodeRelation(); if ((prel != null) && (prel.otherType == dbmap) && (prel.accessName != null)) { @@ -880,7 +882,7 @@ public final class Node implements INode, Serializable { // check if properties are subnodes (_properties.aresubnodes=true) if ((dbmap != null) && (node.dbmap != null)) { - Relation prel = dbmap.getPropertyRelation(); + Relation prel = dbmap.getSubnodeRelation(); if ((prel != null) && (prel.accessName != null)) { Relation localrel = node.dbmap.columnNameToRelation(prel.accessName); @@ -1027,26 +1029,26 @@ public final class Node implements INode, Serializable { Relation rel = dbmap.getExactPropertyRelation(name); if (rel != null) { - return (IPathElement) getNode(name); + return getNode(name); } rel = dbmap.getSubnodeRelation(); if ((rel != null) && (rel.groupby == null) && (rel.accessName != null)) { if ((rel.otherType != null) && rel.otherType.isRelational()) { - return (IPathElement) nmgr.getNode(this, name, rel); + return nmgr.getNode(this, name, rel); } else { - return (IPathElement) getNode(name); + return getNode(name); } } - return (IPathElement) getSubnode(name); + return getSubnode(name); } else { // no dbmapping - just try child collection first, then named property. - IPathElement child = (IPathElement) getSubnode(name); + IPathElement child = getSubnode(name); if (child == null) { - child = (IPathElement) getNode(name); + child = getNode(name); } return child; @@ -1266,7 +1268,7 @@ public final class Node implements INode, Serializable { // check if subnodes are also accessed as properties. If so, also unset the property if ((dbmap != null) && (node.dbmap != null)) { - Relation prel = dbmap.getPropertyRelation(); + Relation prel = dbmap.getSubnodeRelation(); if ((prel != null) && (prel.accessName != null)) { Relation localrel = node.dbmap.columnNameToRelation(prel.accessName); @@ -1552,7 +1554,7 @@ public final class Node implements INode, Serializable { return dbmap.getPropertyEnumeration(); } - Relation prel = (dbmap == null) ? null : dbmap.getPropertyRelation(); + Relation prel = (dbmap == null) ? null : dbmap.getSubnodeRelation(); if ((prel != null) && prel.hasAccessName() && (prel.otherType != null) && prel.otherType.isRelational()) { @@ -1600,89 +1602,97 @@ public final class Node implements INode, Serializable { getParent(); } + /** + * + * + * @param propname ... + * + * @return ... + */ protected Property getProperty(String propname) { - // nmgr.logEvent ("GETTING PROPERTY: "+propname); if (propname == null) { return null; } + + Relation rel = dbmap == null ? + null : + dbmap.getExactPropertyRelation(propname); + + // 1) check if this is a create-on-demand node property + if (rel != null && (rel.isCollection() || rel.isComplexReference())) { + if (state == TRANSIENT && rel.isCollection()) { + // When we get a collection from a transient node for the first time, or when + // we get a collection whose content objects are stored in the embedded + // XML data storage, we just want to create and set a generic node without + // consulting the NodeManager about it. + Node n = new Node(propname, rel.getPrototype(), nmgr); + n.setDbMapping(rel.getVirtualMapping()); + n.setParent(this); + setNode(propname, n); + return (Property) propMap.get(propname.toLowerCase()); + } else if (state != TRANSIENT) { + Node n = nmgr.getNode(this, propname, rel); - Property prop = (propMap == null) ? null - : (Property) propMap.get(propname.toLowerCase()); - - // See if this could be a relationally linked node which still doesn't know - // (i.e, still thinks it's just the key as a string) - DbMapping pmap = (dbmap == null) ? null : dbmap.getExactPropertyMapping(propname); - - if ((pmap != null) && (prop != null) && (prop.getType() != IProperty.NODE)) { - // this is a relational node stored by id but we still think it's just a string. - // Fix it. - prop.convertToNodeReference(pmap); - } - - // the property does not exist in our propmap - see if we should create it on the fly, - // either because it is mapped to an object from relational database or defined as - // collection aka virtual node - if (dbmap != null) { - // the explicitly defined property mapping - Relation propRel = dbmap.getPropertyRelation(propname); - - // property was not found in propmap - if (prop == null) { - // if no property relation is defined for this specific property name, - // use the generic property relation, if one is defined. - if (propRel == null) { - propRel = dbmap.getPropertyRelation(); - } - - // so if we have a property relation and it does in fact link to another object... - if ((propRel != null) && (propRel.isCollection() || - propRel.isComplexReference())) { - // in some cases we just want to create and set a generic node without - // consulting the NodeManager if it exists: When we get a collection - // (aka virtual node) from a transient node for the first time, or when - // we get a collection whose content objects are stored in the embedded - // XML data storage. - if ((state == TRANSIENT) && propRel.virtual) { - Node pn = new Node(propname, propRel.getPrototype(), nmgr); - - pn.setDbMapping(propRel.getVirtualMapping()); - pn.setParent(this); - setNode(propname, pn); - prop = (Property) propMap.get(propname); + if (n != null) { + if ((n.parentHandle == null) && + !"root".equalsIgnoreCase(n.getPrototype())) { + n.setParent(this); + n.name = propname; + n.anonymous = false; } - // if this is from relational database only fetch if this node - // is itself persistent. - else if ((state != TRANSIENT) && propRel.createOnDemand()) { - // this may be a relational node stored by property name - Node pn = nmgr.getNode(this, propname, propRel); - - if (pn != null) { - if ((pn.parentHandle == null) && - !"root".equalsIgnoreCase(pn.getPrototype())) { - pn.setParent(this); - pn.name = propname; - pn.anonymous = false; - } - - prop = new Property(propname, this, pn); - } - } - } - } else if (propRel != null && propRel.isVirtual()) { - // prop was found and explicit property relation is collection - - // this is a collection node containing objects stored in the embedded db - Node pn = (Node) prop.getNodeValue(); - if (pn != null) { - // do set DbMapping for embedded db collection nodes - pn.setDbMapping(propRel.getVirtualMapping()); - // also set node manager in case this is a mountpoint node - // that came in through replication - pn.nmgr = nmgr; + return new Property(propname, this, n); } } } - return prop; + // 2) check if the property is contained in the propMap + Property prop = propMap == null ? null : + (Property) propMap.get(propname.toLowerCase()); + + if (prop != null) { + if (rel != null) { + // Is a relational node stored by id but things it's a string or int. Fix it. + if (rel.otherType != null && prop.getType() != Property.NODE) { + prop.convertToNodeReference(rel.otherType); + } + if (rel.isCollection()) { + // property was found in propMap and is a collection - this is + // a collection holding non-relational objects. set DbMapping and + // NodeManager + Node n = (Node) prop.getNodeValue(); + if (n != null) { + // do set DbMapping for embedded db collection nodes + n.setDbMapping(rel.getVirtualMapping()); + // also set node manager in case this is a mountpoint node + // that came in through replication + n.nmgr = nmgr; + } + } + } + return prop; + } + + // 3) try to get the property from the database via accessname, if defined + if (rel == null && dbmap != null && state != TRANSIENT) { + rel = dbmap.getSubnodeRelation(); + + if (rel != null && rel.otherType != null) { + Node n = nmgr.getNode(this, propname, rel); + + if (n != null) { + if ((n.parentHandle == null) && + !"root".equalsIgnoreCase(n.getPrototype())) { + n.setParent(this); + n.name = propname; + n.anonymous = false; + } + return new Property(propname, this, n); + } + } + } + + // 4) nothing to be found - return null + return null; } /** @@ -1896,7 +1906,7 @@ public final class Node implements INode, Serializable { // check if this node is already registered with the old name; if so, remove it. // then set parent's property to this node for the new name value DbMapping parentmap = parent.getDbMapping(); - Relation propRel = parentmap.getPropertyRelation(); + Relation propRel = parentmap.getSubnodeRelation(); String dbcolumn = dbmap.propertyToColumnName(propname); if ((propRel != null) && (propRel.accessName != null) && diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index 0949a806..2f4b01ff 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -19,8 +19,11 @@ package helma.objectmodel.db; import helma.framework.core.Application; import helma.objectmodel.*; import helma.util.CacheMap; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; import java.math.BigDecimal; -import java.io.*; import java.sql.*; import java.util.*; @@ -215,24 +218,21 @@ public final class NodeManager { if ((node == null) || (node.getState() == Node.INVALID)) { // The requested node isn't in the shared cache. - // Synchronize with key to make sure only one version is - // fetched from the database. if (key instanceof SyntheticKey) { Node parent = getNode(key.getParentKey()); Relation rel = parent.dbmap.getPropertyRelation(key.getID()); - if ((rel == null) || (rel.groupby != null)) { - node = parent.getGroupbySubnode(key.getID(), true); - } else if (rel != null) { - node = getNode(parent, key.getID(), rel); + if (rel != null) { + return getNode(parent, key.getID(), rel); } else { node = null; } - } else { + } else if (key instanceof DbKey) { node = getNodeByKey(tx.txn, (DbKey) key); } if (node != null) { + // synchronize with cache synchronized (cache) { Node oldnode = (Node) cache.put(node.getKey(), node); @@ -241,8 +241,8 @@ public final class NodeManager { cache.put(node.getKey(), oldnode); node = oldnode; } - } - // synchronized + } + // end of cache-synchronized section } } @@ -279,10 +279,9 @@ public final class NodeManager { // generated on the fly key = new SyntheticKey(home.getKey(), kstr); } else { - // if a key for a node from within the DB - // FIXME: This should never apply, since for every relation-based loading - // Synthetic Keys are used. Right? + // refers to a node through its primary database key key = new DbKey(rel.otherType, kstr); + return getNode(key); } // See if Transactor has already come across this node diff --git a/src/helma/objectmodel/db/Relation.java b/src/helma/objectmodel/db/Relation.java index a50245e9..ab48eb38 100644 --- a/src/helma/objectmodel/db/Relation.java +++ b/src/helma/objectmodel/db/Relation.java @@ -17,12 +17,13 @@ package helma.objectmodel.db; import helma.framework.core.Application; -import helma.objectmodel.*; +import helma.objectmodel.INode; + import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; import java.util.Vector; -import java.util.Map; -import java.util.HashMap; /** * This describes how a property of a persistent Object is stored in a @@ -119,7 +120,7 @@ public final class Relation { /** * Reads a relation entry from a line in a properties file. */ - public Relation(String desc, String propName, DbMapping ownType, Properties props) { + public Relation(String propName, DbMapping ownType) { this.ownType = ownType; this.propName = propName; otherType = null; @@ -423,6 +424,10 @@ public final class Relation { * - complex reference nodes */ public boolean createOnDemand() { + if (otherType == null) { + return false; + } + return virtual || (otherType.isRelational() && accessName != null) || (groupby != null) || isComplexReference(); diff --git a/src/helma/scripting/rhino/HopObject.java b/src/helma/scripting/rhino/HopObject.java index 30369e07..b1808640 100644 --- a/src/helma/scripting/rhino/HopObject.java +++ b/src/helma/scripting/rhino/HopObject.java @@ -17,7 +17,6 @@ package helma.scripting.rhino; import helma.scripting.ScriptingException; -import helma.framework.*; import helma.framework.core.*; import helma.objectmodel.*; import helma.objectmodel.db.*; @@ -28,7 +27,6 @@ import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; -import java.util.HashMap; import java.util.Map; /** @@ -192,8 +190,8 @@ public class HopObject extends ScriptableObject implements Wrapper { /** * Render a skin to the response buffer. * - * @param skin The skin object or name - * @param param An optional parameter object + * @param skinobj The skin object or name + * @param paramobj An optional parameter object * * @return ... */ @@ -221,8 +219,8 @@ public class HopObject extends ScriptableObject implements Wrapper { /** * Render a skin and return its output as string. * - * @param skin The skin object or name - * @param param An optional parameter object + * @param skinobj The skin object or name + * @param paramobj An optional parameter object * * @return ... */ @@ -295,19 +293,8 @@ public class HopObject extends ScriptableObject implements Wrapper { if (id instanceof Number) { n = get(((Number) id).intValue(), this); - } else if (id instanceof String) { - // HopObject.get() is more often called for child elements than for - // ordinary properties, so try a getChildElement() first. This seems - // to have quite an impact on get() performance. - n = node.getChildElement(id.toString()); - - if (n != null) { - return Context.toObject(n, core.global); - } - + } else if (id != null) { n = getFromNode(id.toString()); - } else { - throw new RuntimeException("Invalid type for id argument in HopObject.get(): "+id); } // since we're calling Scriptable.get() methods, we'll get NOT_FOUND rather @@ -350,7 +337,7 @@ public class HopObject extends ScriptableObject implements Wrapper { */ public boolean jsFunction_set(Object id, Object value) { if (id == Undefined.instance || value == Undefined.instance) { - throw new RuntimeException("HopObject.set() called with wrong number of arguments"); + throw new EvaluatorException("HopObject.set() called with wrong number of arguments"); } if ((node == null)) { return false; @@ -361,7 +348,7 @@ public class HopObject extends ScriptableObject implements Wrapper { if (id instanceof Number) { if (!(value instanceof HopObject)) { - throw new RuntimeException("Can only set HopObjects as child objects in HopObject.set()"); + throw new EvaluatorException("Can only set HopObjects as child objects in HopObject.set()"); } int idx = (((Number) id).intValue()); @@ -369,10 +356,8 @@ public class HopObject extends ScriptableObject implements Wrapper { node.addNode(n, idx); - } else if (id instanceof String) { + } else if (id != null) { put(id.toString(), this, value); - } else { - throw new RuntimeException("Invalid type for id argument in HopObject.set(): "+id); } return true; @@ -844,7 +829,7 @@ public class HopObject extends ScriptableObject implements Wrapper { /** * * - * @param name ... + * @param idx ... * @param start ... * * @return ... @@ -862,7 +847,7 @@ public class HopObject extends ScriptableObject implements Wrapper { /** * * - * @param name ... + * @param idx ... * @param start ... * * @return ...