switched everything from the old id/mapping to the new

NodeHandle references.
This breaks serialization compatibility with prior versions.
This commit is contained in:
hns 2001-08-03 14:54:57 +00:00
parent 7bdcdcd65f
commit a90c945bae

View file

@ -25,8 +25,8 @@ import com.workingdogs.village.*;
public class Node implements INode, Serializable { public class Node implements INode, Serializable {
// The ID of this node's parent node // The handle to the node's parent
protected String parentID; protected NodeHandle parentHandle;
// Ordered list of subnodes of this node // Ordered list of subnodes of this node
private List subnodes; private List subnodes;
// Named subnodes (properties) of this node // Named subnodes (properties) of this node
@ -52,7 +52,11 @@ public class Node implements INode, Serializable {
int version = in.readShort (); int version = in.readShort ();
id = in.readUTF (); id = in.readUTF ();
name = in.readUTF (); name = in.readUTF ();
parentID = (String) in.readObject (); if (version < 5)
throw new IOException ("Unsupported Data Format (pre 1.2)");
// parentHandle = new NodeHandle (new Key (null, (String) in.readObject ()));
else
parentHandle = (NodeHandle) in.readObject ();
created = in.readLong (); created = in.readLong ();
lastmodified = in.readLong (); lastmodified = in.readLong ();
if (version < 4) { if (version < 4) {
@ -75,10 +79,10 @@ public class Node implements INode, Serializable {
} }
private void writeObject (ObjectOutputStream out) throws IOException { private void writeObject (ObjectOutputStream out) throws IOException {
out.writeShort (4); // serialization version out.writeShort (5); // serialization version
out.writeUTF (id); out.writeUTF (id);
out.writeUTF (name); out.writeUTF (name);
out.writeObject (parentID); out.writeObject (parentHandle);
out.writeLong (created); out.writeLong (created);
out.writeLong (lastmodified); out.writeLong (lastmodified);
DbMapping smap = dbmap == null ? null : dbmap.getSubnodeMapping (); DbMapping smap = dbmap == null ? null : dbmap.getSubnodeMapping ();
@ -93,12 +97,18 @@ public class Node implements INode, Serializable {
out.writeObject (prototype); out.writeObject (prototype);
} }
transient String prototype; private transient String prototype;
transient INode cacheNode;
private transient NodeHandle handle;
private transient INode cacheNode;
transient WrappedNodeManager nmgr; transient WrappedNodeManager nmgr;
transient DbMapping dbmap; transient DbMapping dbmap;
transient Key primaryKey = null; transient Key primaryKey = null;
transient DbMapping parentmap;
transient String subnodeRelation = null; transient String subnodeRelation = null;
transient long lastSubnodeFetch = 0; transient long lastSubnodeFetch = 0;
transient long lastSubnodeChange = 0; transient long lastSubnodeChange = 0;
@ -117,12 +127,27 @@ public class Node implements INode, Serializable {
static final long serialVersionUID = -3740339688506633675L; static final long serialVersionUID = -3740339688506633675L;
/** /**
* This constructor is only used for instances of the NullNode subclass. Do not use for ordinary Nodes! * This constructor is only used for instances of the NullNode subclass. Do not use for ordinary Nodes!<
*/ */
public Node () { Node () {
created = lastmodified = System.currentTimeMillis (); created = lastmodified = System.currentTimeMillis ();
} }
/**
* Creates a new Node with the given name. Only used by NodeManager for "root nodes" and
* not in a Transaction context, which is why we can immediately mark it as CLEAN.
*/
protected Node (String name, String id, String prototype, WrappedNodeManager nmgr) {
this.nmgr = nmgr;
this.id = id;
this.name = name == null || "".equals (name) ? id : name;
if (prototype != null)
setPrototype (prototype);
created = lastmodified = System.currentTimeMillis ();
markAs (CLEAN);
}
/** /**
* Constructor used for virtual nodes. * Constructor used for virtual nodes.
*/ */
@ -131,18 +156,38 @@ public class Node implements INode, Serializable {
setParent (home); setParent (home);
// this.dbmap = null; // this.dbmap = null;
// generate a key for the virtual node that can't be mistaken for a JDBC-URL // generate a key for the virtual node that can't be mistaken for a JDBC-URL
this.id = Key.makeVirtualID (parentmap, parentID, propname); primaryKey = new SyntheticKey (home.getKey (), propname);
this.id = primaryKey.getID ();
this.name = propname; this.name = propname;
this.anonymous = false; this.anonymous = false;
setPrototype (prototype); setPrototype (prototype);
this.state = VIRTUAL; this.state = VIRTUAL;
} }
/**
* Creates a new Node with the given name. This is used for ordinary transient nodes.
*/
public Node (String n, String prototype, WrappedNodeManager nmgr) {
this.nmgr = nmgr;
this.prototype = prototype;
dbmap = nmgr.getDbMapping (prototype);
// the id is only generated when the node is actually checked into db.
// id = nmgr.generateID (dbmap);
// checkWriteLock ();
this.name = n == null ? "" : n;
created = lastmodified = System.currentTimeMillis ();
adoptName = true;
state = TRANSIENT;
// markAs (TRANSIENT);
}
/** /**
* Constructor used for nodes being stored in a relational database table. * Constructor used for nodes being stored in a relational database table.
*/ */
public Node (DbMapping dbmap, Record rec, WrappedNodeManager nmgr) throws DataSetException { public Node (DbMapping dbmap, Record rec, WrappedNodeManager nmgr) throws DataSetException {
this.nmgr = nmgr;
// see what prototype/DbMapping this object should use // see what prototype/DbMapping this object should use
DbMapping m = dbmap; DbMapping m = dbmap;
String protoField= dbmap.getPrototypeField (); String protoField= dbmap.getPrototypeField ();
@ -161,7 +206,6 @@ public class Node implements INode, Serializable {
setPrototype (m.getTypeName ()); setPrototype (m.getTypeName ());
this.dbmap = m; this.dbmap = m;
this.nmgr = nmgr;
id = rec.getValue (dbmap.getIDField ()).asString (); id = rec.getValue (dbmap.getIDField ()).asString ();
checkWriteLock (); checkWriteLock ();
String nameField = dbmap.getNameField (); String nameField = dbmap.getNameField ();
@ -170,8 +214,7 @@ public class Node implements INode, Serializable {
name = m.getTypeName() + " " + id; name = m.getTypeName() + " " + id;
// set parent for user objects to internal userroot node // set parent for user objects to internal userroot node
if ("user".equals (prototype)) { if ("user".equals (prototype)) {
this.parentID = "1"; parentHandle = new NodeHandle (new DbKey (null, "1"));
this.parentmap = nmgr.getDbMapping("__userroot__");
anonymous = false; anonymous = false;
} }
@ -255,7 +298,7 @@ public class Node implements INode, Serializable {
// if the property is a pointer to another node, change the property type to NODE // if the property is a pointer to another node, change the property type to NODE
if (rel.direction == Relation.FORWARD) { if (rel.direction == Relation.FORWARD) {
newprop.nvalueID = newprop.getStringValue (); newprop.nhandle = new NodeHandle (new DbKey (rel.other, newprop.getStringValue ()));
newprop.type = IProperty.NODE; newprop.type = IProperty.NODE;
} }
} }
@ -263,93 +306,6 @@ public class Node implements INode, Serializable {
} }
/**
* Creates a new Node with the given name.
*/
public Node (String n, String prototype, WrappedNodeManager nmgr) {
this.nmgr = nmgr;
this.prototype = prototype;
dbmap = nmgr.getDbMapping (prototype);
id = nmgr.generateID (dbmap);
// checkWriteLock ();
this.name = n == null || "".equals (n) ? id : n;
created = lastmodified = System.currentTimeMillis ();
adoptName = true;
markAs (TRANSIENT);
// nmgr.registerNode (this);
}
/**
* Creates a new Node with the given name. Only used by NodeManager for "root nodes" and
* not in a Transaction context, which is why we can immediately mark it as CLEAN.
*/
protected Node (String name, String id, String prototype, WrappedNodeManager nmgr) {
this.nmgr = nmgr;
this.id = id;
this.name = name == null || "".equals (name) ? id : name;
if (prototype != null)
setPrototype (prototype);
created = lastmodified = System.currentTimeMillis ();
markAs (CLEAN);
}
/**
* Creates a new instance of Node, transforming from another implementation of
* interface INode. This Constructor is used when a transient
* node is converted into a persistent-capable one, hence the status is set to NEW.
*/
/* private Node (INode node, Hashtable ntable, boolean conversionRoot, WrappedNodeManager nmgr) {
this.nmgr = nmgr;
this.dbmap = node.getDbMapping ();
this.id = nmgr.generateID (dbmap);
checkWriteLock ();
this.name = node.getName ();
this.prototype = node.getPrototype ();
created = lastmodified = System.currentTimeMillis ();
ntable.put (node, this);
// only take over name from property if this is not the root of the current node conversion
adoptName = !conversionRoot;
for (Enumeration e = node.getSubnodes (); e.hasMoreElements (); ) {
INode next = (INode) e.nextElement ();
Node nextc = (next instanceof Node) ? (Node) next : (Node) ntable.get (next); // is this a Node already?
if (nextc == null)
nextc = new Node (next, ntable, true, nmgr);
if (nextc.state == INVALID)
nextc = nmgr.getNode (nextc.getID (), nextc.getDbMapping ());
addNode (nextc);
}
for (Enumeration e = node.properties (); e.hasMoreElements (); ) {
IProperty next = node.get ((String) e.nextElement (), false);
if (next == null)
continue;
int t = next.getType ();
if (t == IProperty.NODE) {
INode n = next.getNodeValue ();
Node nextc = (n instanceof Node) ? (Node) n : (Node) ntable.get (n); // is this a Node already?
if (nextc == null)
nextc = new Node (n, ntable, true, nmgr);
if (nextc.state == INVALID)
nextc = nmgr.getNode (nextc.getID (), nextc.getDbMapping ());
setNode (next.getName (), nextc);
} else if (t == IProperty.STRING) {
setString (next.getName (), next.getStringValue ());
} else if (t == IProperty.INTEGER) {
setInteger (next.getName (), next.getIntegerValue ());
} else if (t == IProperty.FLOAT) {
setFloat (next.getName (), next.getFloatValue ());
} else if (t == IProperty.BOOLEAN) {
setBoolean (next.getName (), next.getBooleanValue ());
} else if (t == IProperty.DATE) {
setDate (next.getName (), next.getDateValue ());
} else if (t == IProperty.JAVAOBJECT) {
setJavaObject (next.getName (), next.getJavaObjectValue ());
}
}
adoptName = true; // switch back to normal name adoption behaviour
markAs (NEW);
// nmgr.registerNode (this);
} */
protected synchronized void checkWriteLock () { protected synchronized void checkWriteLock () {
// System.err.println ("registering writelock for "+this.getName ()+" ("+lock+") to "+Thread.currentThread ()); // System.err.println ("registering writelock for "+this.getName ()+" ("+lock+") to "+Thread.currentThread ());
@ -422,6 +378,8 @@ public class Node implements INode, Serializable {
*/ */
public String getID () { public String getID () {
if (state == TRANSIENT)
throw new RuntimeException ("getID called on transient Node: "+this);
return id; return id;
} }
@ -445,20 +403,23 @@ public class Node implements INode, Serializable {
public String getNameOrID () { public String getNameOrID () {
// if subnodes are also mounted as properties, try to get the "nice" prop value // if subnodes are also mounted as properties, try to get the "nice" prop value
// instead of the id by turning the anonymous flag off. // instead of the id by turning the anonymous flag off.
// HACK: work around this for user objects to alsways return a URL like /users/username // Work around this for user objects to alsways return a URL like /users/username
if ("user".equalsIgnoreCase (prototype)) { if ("user".equalsIgnoreCase (prototype)) {
anonymous = false; anonymous = false;
} else if (parentmap != null) { } else if (parentHandle != null) {
Relation prel = parentmap.getPropertyRelation(); try {
if (prel != null && prel.subnodesAreProperties && !prel.usesPrimaryKey ()) try { DbMapping parentmap = parentHandle.getDbMapping (nmgr);
Relation localrel = dbmap.columnNameToProperty (prel.getRemoteField ()); Relation prel = parentmap.getPropertyRelation();
String propvalue = getString (localrel.propname, false); if (prel != null && prel.subnodesAreProperties && !prel.usesPrimaryKey ()) {
if (propvalue != null && propvalue.length() > 0) { Relation localrel = dbmap.columnNameToProperty (prel.getRemoteField ());
setName (propvalue); String propvalue = getString (localrel.propname, false);
anonymous = false; if (propvalue != null && propvalue.length() > 0) {
// nameProp = localrel.propname; setName (propvalue);
} else { anonymous = false;
anonymous = true; // nameProp = localrel.propname;
} else {
anonymous = true;
}
} }
} catch (Exception ignore) { } catch (Exception ignore) {
// just fall back to default method // just fall back to default method
@ -533,10 +494,7 @@ public class Node implements INode, Serializable {
public void setDbMapping (DbMapping dbmap) { public void setDbMapping (DbMapping dbmap) {
if (this.dbmap != dbmap) { if (this.dbmap != dbmap) {
this.dbmap = dbmap; this.dbmap = dbmap;
primaryKey = null; // primaryKey = null;
try {
((Transactor) Thread.currentThread()).visitCleanNode (this);
} catch (ClassCastException ignore) {}
} }
} }
@ -545,13 +503,21 @@ public class Node implements INode, Serializable {
} }
public Key getKey () { public Key getKey () {
if (state == TRANSIENT)
throw new RuntimeException ("getKey called on transient Node: "+this);
if (dbmap == null && prototype != null && nmgr != null) if (dbmap == null && prototype != null && nmgr != null)
dbmap = nmgr.getDbMapping (prototype); dbmap = nmgr.getDbMapping (prototype);
if (primaryKey == null) if (primaryKey == null)
primaryKey = new Key (dbmap, id); primaryKey = new DbKey (dbmap, id);
return primaryKey; return primaryKey;
} }
public NodeHandle getHandle () {
if (handle == null)
handle = new NodeHandle (this);
return handle;
}
public void setSubnodeRelation (String rel) { public void setSubnodeRelation (String rel) {
if ((rel == null && this.subnodeRelation == null) if ((rel == null && this.subnodeRelation == null)
|| (rel != null && rel.equalsIgnoreCase (this.subnodeRelation))) || (rel != null && rel.equalsIgnoreCase (this.subnodeRelation)))
@ -584,8 +550,7 @@ public class Node implements INode, Serializable {
* the ID + DB map combo. * the ID + DB map combo.
*/ */
public void setParent (Node parent) { public void setParent (Node parent) {
this.parentID = parent == null ? null : parent.getID(); parentHandle = parent == null ? null : parent.getHandle ();
this.parentmap = parent == null ? null : parent.getDbMapping();
} }
/** /**
@ -595,21 +560,21 @@ public class Node implements INode, Serializable {
*/ */
public void setParent (Node parent, String propertyName) { public void setParent (Node parent, String propertyName) {
// we only do that for relational nodes. // we only do that for relational nodes.
if (dbmap == null || !dbmap.isRelational ()) if (!isRelational ())
return; return;
String oldParentID = parentID; NodeHandle oldParentHandle = parentHandle;
parentID = parent == null ? null : parent.getID(); parentHandle = parent == null ? null : parent.getHandle ();
parentmap = parent == null ? null : parent.getDbMapping();
if (parentID == null || parentID.equals (oldParentID)) if (parentHandle == null || parentHandle.equals (oldParentHandle))
// nothing changed, no need to find access property // nothing changed, no need to find access property
return; return;
if (propertyName == null) { if (parent != null && propertyName == null) {
// see if we can find out the propertyName by ourselfes by looking at the // see if we can find out the propertyName by ourselfes by looking at the
// parent's property relation // parent's property relation
String newname = null; String newname = null;
DbMapping parentmap = parent.getDbMapping ();
if (parentmap != null) { if (parentmap != null) {
// first try to retrieve name via generic property relation of parent // first try to retrieve name via generic property relation of parent
Relation prel = parentmap.getPropertyRelation (); Relation prel = parentmap.getPropertyRelation ();
@ -645,8 +610,7 @@ public class Node implements INode, Serializable {
// check what's specified in the type.properties for this node. // check what's specified in the type.properties for this node.
ParentInfo[] parentInfo = null; ParentInfo[] parentInfo = null;
if (dbmap != null && dbmap.isRelational () && if (isRelational () && (lastParentSet < dbmap.getLastTypeChange() || lastParentSet < lastmodified))
(lastParentSet < dbmap.getLastTypeChange() || lastParentSet < lastmodified))
parentInfo = dbmap.getParentInfo (); parentInfo = dbmap.getParentInfo ();
// check if current parent candidate matches presciption, if not, try to get it // check if current parent candidate matches presciption, if not, try to get it
@ -679,10 +643,9 @@ public class Node implements INode, Serializable {
} }
// fall back to heuristic parent (the node that fetched this one from db) // fall back to heuristic parent (the node that fetched this one from db)
if (parentID == null) { if (parentHandle == null)
return null; return null;
} return parentHandle.getNode (nmgr);
return nmgr.getNode (parentID, parentmap);
} }
@ -690,9 +653,9 @@ public class Node implements INode, Serializable {
* Get parent, using cached info if it exists. * Get parent, using cached info if it exists.
*/ */
public Node getCachedParent () { public Node getCachedParent () {
if (parentID == null) if (parentHandle == null)
return null; return null;
return nmgr.getNode (parentID, parentmap); return parentHandle.getNode (nmgr);
} }
@ -758,22 +721,17 @@ public class Node implements INode, Serializable {
if (where < 0 || where > numberOfNodes ()) if (where < 0 || where > numberOfNodes ())
where = numberOfNodes (); where = numberOfNodes ();
if (node.parentID != null) {
// this makes the job of addLink, which means that addLink and addNode
// are functionally equivalent now.
if (!node.parentID.equals (id) || !node.anonymous) {
node.registerLink (this);
}
}
if (subnodes != null && subnodes.contains (node.getID ())) { NodeHandle nhandle = node.getHandle ();
if (subnodes != null && subnodes.contains (nhandle)) {
// Node is already subnode of this - just move to new position // Node is already subnode of this - just move to new position
subnodes.remove (node.getID ()); subnodes.remove (nhandle);
where = Math.min (where, numberOfNodes ()); where = Math.min (where, numberOfNodes ());
subnodes.add (where, node.getID ()); subnodes.add (where, nhandle);
} else { } else {
if (subnodes == null) subnodes = new ExternalizableVector (); if (subnodes == null)
subnodes.add (where, node.getID ()); subnodes = new ExternalizableVector ();
subnodes.add (where, nhandle);
// check if properties are subnodes (_properties.aresubnodes=true) // check if properties are subnodes (_properties.aresubnodes=true)
if (dbmap != null && node.dbmap != null) { if (dbmap != null && node.dbmap != null) {
@ -795,9 +753,19 @@ public class Node implements INode, Serializable {
} }
} }
if (node.parentID == null && !"root".equalsIgnoreCase (node.getPrototype ())) { if (!"root".equalsIgnoreCase (node.getPrototype ())) {
node.setParent (this); Node nparent = (Node) node.getParent ();
node.anonymous = true; // if the node doesn't have a parent yet, or it has one but it's transient while we are
// persistent, make this the nodes new parent.
if (nparent == null || (state != TRANSIENT && nparent.getState () == TRANSIENT)) {
node.setParent (this);
node.anonymous = true;
} else if (nparent != null && (nparent != this || !node.anonymous)) {
// this makes the additional job of addLink, registering that we have a link to a node in our
// subnodes that actually sits somewhere else. This means that addLink and addNode
// are actually the same now.
node.registerLinkFrom (this);
}
} }
} }
@ -856,14 +824,18 @@ public class Node implements INode, Serializable {
/** /**
* register a node that links to this node. * register a node that links to this node so we can notify it when we cease to exist.
* this is only necessary if we are a non-relational node, since for relational nodes
* the referring object will notice that we've gone at runtime.
*/ */
protected void registerLink (Node from) { protected void registerLinkFrom (Node from) {
if (isRelational ())
return;
if (links == null) if (links == null)
links = new ExternalizableVector (); links = new ExternalizableVector ();
Object fromID = from.getID (); Object fromHandle = from.getHandle ();
if (!links.contains (fromID)) if (!links.contains (fromHandle))
links.add (fromID); links.add (fromHandle);
} }
public INode getSubnode (String path) { public INode getSubnode (String path) {
@ -878,25 +850,28 @@ public class Node implements INode, Serializable {
retval = this; retval = this;
} else { } else {
runner.loadNodes (); runner.loadNodes ();
boolean found = runner.subnodes == null ? false : runner.subnodes.contains (next); Relation srel = null;
DbMapping smap = null;
if (!found) if (runner.dbmap != null) {
retval = null; srel = runner.dbmap.getSubnodeRelation ();
else { smap = runner.dbmap.getSubnodeMapping ();
Relation srel = null;
DbMapping smap = null;
if (runner.dbmap != null) {
srel = runner.dbmap.getSubnodeRelation ();
smap = runner.dbmap.getSubnodeMapping ();
}
// check if there is a group-by relation
if (srel != null && srel.groupby != null)
retval = nmgr.getNode (this, next, srel);
else
retval = nmgr.getNode (next, smap);
} }
if (retval != null && retval.parentID == null && !"root".equalsIgnoreCase (retval.getPrototype ())) { // check if there is a group-by relation
NodeHandle nhandle = null;
if (srel != null && srel.groupby != null)
nhandle = new NodeHandle (new SyntheticKey (runner.getKey (), next));
else
nhandle = new NodeHandle (new DbKey (smap, next));
boolean found = runner.subnodes == null ? false : runner.subnodes.contains (nhandle);
if (!found) {
retval = null;
} else {
retval = nhandle.getNode (nmgr);
}
if (retval != null && retval.parentHandle == null && !"root".equalsIgnoreCase (retval.getPrototype ())) {
retval.setParent (runner); retval.setParent (runner);
retval.anonymous = true; retval.anonymous = true;
} }
@ -914,20 +889,16 @@ public class Node implements INode, Serializable {
if (subnodes == null) if (subnodes == null)
return null; return null;
Relation srel = null;
DbMapping smap = null; DbMapping smap = null;
if (dbmap != null) { if (dbmap != null)
srel = dbmap.getSubnodeRelation ();
smap = dbmap.getSubnodeMapping (); smap = dbmap.getSubnodeMapping ();
}
Node retval = null; Node retval = null;
if (subnodes.size () > index) { if (subnodes.size () > index) {
// check if there is a group-by relation // check if there is a group-by relation
if (srel != null && srel.groupby != null) retval = ((NodeHandle) subnodes.get (index)).getNode (nmgr);
retval = nmgr.getNode (this, (String) subnodes.get (index), srel);
else if (retval != null && retval.parentHandle == null && !"root".equalsIgnoreCase (retval.getPrototype ())) {
retval = nmgr.getNode ((String) subnodes.get (index), smap);
if (retval != null && retval.parentID == null && !"root".equalsIgnoreCase (retval.getPrototype ())) {
retval.setParent (this); retval.setParent (this);
retval.anonymous = true; retval.anonymous = true;
} }
@ -940,7 +911,8 @@ public class Node implements INode, Serializable {
if (subnodes == null) if (subnodes == null)
subnodes = new ExternalizableVector (); subnodes = new ExternalizableVector ();
if (subnodes.contains (sid) || create) try { NodeHandle ghandle = new NodeHandle (new SyntheticKey (getKey(), sid));
if (subnodes.contains (ghandle) || create) try {
Relation srel = dbmap.getSubnodeRelation (); Relation srel = dbmap.getSubnodeRelation ();
Relation prel = dbmap.getPropertyRelation (); Relation prel = dbmap.getPropertyRelation ();
boolean relational = srel.other != null && srel.other.isRelational (); boolean relational = srel.other != null && srel.other.isRelational ();
@ -976,7 +948,7 @@ public class Node implements INode, Serializable {
node.setPrototype (gsrel.prototype); node.setPrototype (gsrel.prototype);
} else { } else {
setNode (sid, node); setNode (sid, node);
subnodes.add (node.getID ()); subnodes.add (node.getHandle ());
} }
nmgr.evictKey (node.getKey ()); nmgr.evictKey (node.getKey ());
return node; return node;
@ -1014,7 +986,7 @@ public class Node implements INode, Serializable {
} else { } else {
// removed just a link, not the main node. // removed just a link, not the main node.
if (n.links != null) { if (n.links != null) {
n.links.remove (this.id); n.links.remove (getHandle ());
if (n.state == CLEAN) n.markAs (MODIFIED); if (n.state == CLEAN) n.markAs (MODIFIED);
} }
} }
@ -1026,7 +998,7 @@ public class Node implements INode, Serializable {
*/ */
protected void releaseNode (Node node) { protected void releaseNode (Node node) {
if (subnodes != null) if (subnodes != null)
subnodes.remove (node.getID ()); subnodes.remove (node.getHandle ());
lastSubnodeChange = System.currentTimeMillis (); lastSubnodeChange = System.currentTimeMillis ();
@ -1041,7 +1013,7 @@ public class Node implements INode, Serializable {
} }
} }
// check if subnodes are handled as virtual fs // check if subnodes are also accessed as properties. If so, also unset the property
if (dbmap != null && node.dbmap != null) { if (dbmap != null && node.dbmap != null) {
Relation prel = dbmap.getPropertyRelation(); Relation prel = dbmap.getPropertyRelation();
if (prel != null && prel.subnodesAreProperties && !prel.usesPrimaryKey ()) { if (prel != null && prel.subnodesAreProperties && !prel.usesPrimaryKey ()) {
@ -1072,22 +1044,22 @@ public class Node implements INode, Serializable {
*/ */
protected void deepRemoveNode () { protected void deepRemoveNode () {
// notify nodes that link to this node that it is being deleted. // notify nodes that link to this node being deleted.
int l = links == null ? 0 : links.size (); int l = links == null ? 0 : links.size ();
for (int i = 0; i < l; i++) { for (int i = 0; i < l; i++) {
// TODO: solve dbmap problem NodeHandle lhandle = (NodeHandle) links.get (i);
Node link = nmgr.getNode ((String) links.get (i), null); Node link = lhandle.getNode (nmgr);
if (link != null) link.releaseNode (this); if (link != null)
link.releaseNode (this);
} }
// clean up all nodes that use n as a property // clean up all nodes that refer to this as a property
if (proplinks != null) { if (proplinks != null) {
for (Iterator e1 = proplinks.iterator (); e1.hasNext (); ) try { for (Iterator e1 = proplinks.iterator (); e1.hasNext (); ) try {
String pid = (String) e1.next (); NodeHandle phandle = (NodeHandle) e1.next ();
Node pnode = nmgr.getNode (pid, null); Node pnode = phandle.getNode (nmgr);
if (pnode != null) { if (pnode != null)
nmgr.logEvent("Warning: Not unsetting node property of "+pnode.getFullName ()); nmgr.logEvent("Warning: Not unsetting node property of "+pnode.getName ());
}
} catch (Exception ignore) {} } catch (Exception ignore) {}
} }
@ -1113,7 +1085,7 @@ public class Node implements INode, Serializable {
// getParent() is heuristical/implicit for relational nodes, so we don't base // getParent() is heuristical/implicit for relational nodes, so we don't base
// a cascading delete on that criterium for relational nodes. // a cascading delete on that criterium for relational nodes.
Node n = (Node) v.get (i); Node n = (Node) v.get (i);
if (n.dbmap == null || !n.dbmap.isRelational()) if (!n.isRelational())
removeNode (n); removeNode (n);
} }
} }
@ -1130,14 +1102,18 @@ public class Node implements INode, Serializable {
if (subnodes == null) if (subnodes == null)
return -1; return -1;
// if the node contains relational groupby subnodes, the subnodes vector contains the names instead of ids. // if the node contains relational groupby subnodes, the subnodes vector contains the names instead of ids.
Relation srel = dbmap == null ? null : dbmap.getSubnodeRelation (); /* Relation srel = dbmap == null ? null : dbmap.getSubnodeRelation ();
if (srel != null && srel.groupby != null && srel.other != null && srel.other.isRelational ()) { if (srel != null && srel.groupby != null && srel.other != null && srel.other.isRelational ()) {
if (n.getParent () != this) if (n.getParent () != this)
return -1; return -1;
else else
return subnodes.indexOf (n.getName ()); return subnodes.indexOf (new SyntheticKey (getKey (), n.getID ());
} else } else
return subnodes.indexOf (n.getID ()); return subnodes.indexOf (n.getKey ());*/
if (!(n instanceof Node))
return -1;
Node node = (Node) n;
return subnodes.indexOf (node.getHandle ());
} }
/** /**
@ -1251,7 +1227,7 @@ public class Node implements INode, Serializable {
} }
public String getParentInfo () { public String getParentInfo () {
return "anonymous:"+anonymous+",parentID:"+parentID+",parentmap:"+parentmap+",parent:"+getParent(); return "anonymous:"+anonymous+",parentHandle"+parentHandle+",parent:"+getParent();
} }
protected Property getProperty (String propname, boolean inherit) { protected Property getProperty (String propname, boolean inherit) {
@ -1265,31 +1241,41 @@ public class Node implements INode, Serializable {
DbMapping pmap = dbmap == null ? null : dbmap.getExactPropertyMapping (propname); DbMapping pmap = dbmap == null ? null : dbmap.getExactPropertyMapping (propname);
if (pmap != null && prop != null && prop.type != IProperty.NODE) { if (pmap != null && prop != null && prop.type != IProperty.NODE) {
// this is a relational node stored by id but we still think it's just a string. fix it // this is a relational node stored by id but we still think it's just a string. fix it
prop.nvalueID = prop.getStringValue (); prop.nhandle = new NodeHandle (new DbKey (pmap, prop.getStringValue ()));
prop.type = IProperty.NODE; prop.type = IProperty.NODE;
} }
// the property does not exist in our propmap - see if we can create it on the fly,
// either because it is mapped from a relational database or defined as virtual node
if (prop == null && dbmap != null) { if (prop == null && dbmap != null) {
Relation prel = dbmap.getPropertyRelation (propname); Relation prel = dbmap.getPropertyRelation (propname);
if (prel == null) /* if (prel != null && prel.virtual && prel.other != null && !prel.other.isRelational ()) {
prel = dbmap.getPropertyRelation (); Node pn = (Node) createNode (propname);
if (prel != null && (prel.direction == Relation.DIRECT || prel.virtual)) { if (prel.prototype != null) {
// this *may* be a relational node stored by property name pn.setPrototype (prel.prototype);
try {
Node pn = nmgr.getNode (this, propname, prel);
if (pn != null) {
if (pn.parentID == null && !"root".equalsIgnoreCase (pn.getPrototype ())) {
pn.setParent (this);
pn.name = propname;
pn.anonymous = false;
}
prop = new Property (propname, this, pn);
}
} catch (RuntimeException nonode) {
// wasn't a node after all
} }
} prop = (Property) propMap.get (propname);
} else { */
if (prel == null)
prel = dbmap.getPropertyRelation ();
if (prel != null && (prel.direction == Relation.DIRECT || prel.virtual)) {
// this may be a relational node stored by property name
try {
Node pn = nmgr.getNode (this, propname, prel);
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);
}
} catch (RuntimeException nonode) {
// wasn't a node after all
}
}
// }
} }
if (prop == null && inherit && getParent () != null) { if (prop == null && inherit && getParent () != null) {
prop = ((Node) getParent ()).getProperty (propname, inherit); prop = ((Node) getParent ()).getProperty (propname, inherit);
@ -1393,12 +1379,12 @@ public class Node implements INode, Serializable {
// check if this may have an effect on the node's URL when using subnodesAreProperties // check if this may have an effect on the node's URL when using subnodesAreProperties
// but only do this if we already have a parent set, i.e. if we are already stored in the db // but only do this if we already have a parent set, i.e. if we are already stored in the db
INode parent = parentID == null ? null : getParent (); INode parent = parentHandle == null ? null : getParent ();
if (parent != null && parent.getDbMapping() != null) { if (parent != null && parent.getDbMapping() != null) {
// check if this node is already registered with the old name; if so, remove it. // 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 // then set parent's property to this node for the new name value
parentmap = parent.getDbMapping (); DbMapping parentmap = parent.getDbMapping ();
Relation prel = parentmap.getPropertyRelation (); Relation prel = parentmap.getPropertyRelation ();
if (prel != null && prel.subnodesAreProperties && propname.equals (prel.getRemoteField())) { if (prel != null && prel.subnodesAreProperties && propname.equals (prel.getRemoteField())) {
@ -1414,7 +1400,7 @@ public class Node implements INode, Serializable {
parent.unset (oldvalue); parent.unset (oldvalue);
parent.addNode (this); parent.addNode (this);
// let the node cache know this key's not for this node anymore. // let the node cache know this key's not for this node anymore.
nmgr.evictKey (new Key (prel.other, prel.getKeyID (parent, oldvalue))); nmgr.evictKey (new DbKey (prel.other, prel.getKeyID (parent, oldvalue)));
} }
} }
parent.setNode (value, this); parent.setNode (value, this);
@ -1573,7 +1559,7 @@ public class Node implements INode, Serializable {
// check if the main identity of this node is as a named property // check if the main identity of this node is as a named property
// or as an anonymous node in a collection // or as an anonymous node in a collection
if (n.parentID == null && n.adoptName && !"root".equalsIgnoreCase (n.getPrototype ())) { if (n.parentHandle == null && n.adoptName && !"root".equalsIgnoreCase (n.getPrototype ())) {
n.setParent (this); n.setParent (this);
n.name = propname; n.name = propname;
n.anonymous = false; n.anonymous = false;
@ -1587,7 +1573,7 @@ public class Node implements INode, Serializable {
Property prop = (Property) propMap.get (p2); Property prop = (Property) propMap.get (p2);
if (prop != null) { if (prop != null) {
if (prop.type == IProperty.NODE && n.getID ().equals (prop.nvalueID)) { if (prop.type == IProperty.NODE && n.equals (prop.getNodeValue ())) {
// nothing to do, just clean up locks and return // nothing to do, just clean up locks and return
if (state == CLEAN) clearWriteLock (); if (state == CLEAN) clearWriteLock ();
if (n.state == CLEAN) n.clearWriteLock (); if (n.state == CLEAN) n.clearWriteLock ();
@ -1608,10 +1594,10 @@ public class Node implements INode, Serializable {
// check node in with transactor cache // check node in with transactor cache
Transactor tx = (Transactor) Thread.currentThread (); Transactor tx = (Transactor) Thread.currentThread ();
tx.visitCleanNode (new Key (nmap, nID), n); tx.visitCleanNode (new DbKey (nmap, nID), n);
// if the field is not the primary key of the property, also register it // if the field is not the primary key of the property, also register it
if (rel != null && rel.direction == Relation.DIRECT && !rel.getKeyID(this, p2).equals (nID)) { if (rel != null && rel.direction == Relation.DIRECT && !rel.getKeyID(this, p2).equals (nID)) {
Key secKey = new Key (rel.other, rel.getKeyID(this, p2)); Key secKey = new DbKey (rel.other, rel.getKeyID(this, p2));
nmgr.evictKey (secKey); nmgr.evictKey (secKey);
tx.visitCleanNode (secKey, n); tx.visitCleanNode (secKey, n);
} }
@ -1644,21 +1630,21 @@ public class Node implements INode, Serializable {
} catch (Exception ignore) {} } catch (Exception ignore) {}
} }
protected void registerPropLink (INode n) { protected void registerPropLinkFrom (Node n) {
if (isRelational ())
return;
if (proplinks == null) if (proplinks == null)
proplinks = new ExternalizableVector (); proplinks = new ExternalizableVector ();
String plid = n.getID (); Object fromHandle = n.getHandle ();
if (!proplinks.contains (plid)) if (!proplinks.contains (fromHandle))
proplinks.add (n.getID ()); proplinks.add (fromHandle);
if (state == CLEAN || state == DELETED) if (state == CLEAN || state == DELETED)
markAs (MODIFIED); markAs (MODIFIED);
} }
protected void unregisterPropLink (INode n) { protected void unregisterPropLinkFrom (Node n) {
if (proplinks != null) if (proplinks != null)
proplinks.remove (n.getID ()); proplinks.remove (n.getHandle ());
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.NODE_REMOVED));
// Server.throwNodeEvent (new NodeEvent (n, NodeEvent.SUBNODE_REMOVED, this));
if (state == CLEAN) if (state == CLEAN)
markAs (MODIFIED); markAs (MODIFIED);
} }
@ -1716,21 +1702,28 @@ public class Node implements INode, Serializable {
return "HopObject " + name; return "HopObject " + name;
} }
/** /**
* Recursively convert other implementations of INode into helma.objectmodel.db.Node. * Tell whether this node is stored inside a relational db. This doesn't mean
* it actually is stored in a relational db, just that it would be, if the node was
* persistent
*/ */
/* protected Node convert (INode n) { public boolean isRelational () {
Hashtable ntable = new Hashtable (); return dbmap != null && dbmap.isRelational ();
Node converted = new Node (n, ntable, false, nmgr); }
return converted;
} */
/** /**
* Recursively turn node status from TRANSIENT to NEW so that the Transactor will * Recursively turn node status from TRANSIENT to NEW so that the Transactor will
* know it has to insert this node. * know it has to insert this node.
*/ */
protected void makePersistentCapable () { protected void makePersistentCapable () {
if (state == TRANSIENT) {
state = NEW;
id = nmgr.generateID (dbmap);
Transactor current = (Transactor) Thread.currentThread ();
current.visitNode (this);
current.visitCleanNode (this);
}
for (Enumeration e = getSubnodes (); e.hasMoreElements (); ) { for (Enumeration e = getSubnodes (); e.hasMoreElements (); ) {
Node n = (Node) e.nextElement (); Node n = (Node) e.nextElement ();
if (n.state == TRANSIENT) if (n.state == TRANSIENT)
@ -1744,12 +1737,6 @@ public class Node implements INode, Serializable {
n.makePersistentCapable (); n.makePersistentCapable ();
} }
} }
if (state == TRANSIENT) {
state = NEW;
Transactor current = (Transactor) Thread.currentThread ();
current.visitNode (this);
current.visitCleanNode (this);
}
} }
@ -1775,8 +1762,11 @@ public class Node implements INode, Serializable {
return null; return null;
} }
public void dumpSubnodes () { public void dump () {
System.err.println (subnodes); System.err.println ("subnodes: "+subnodes);
System.err.println ("properties: "+propMap);
System.err.println ("links: "+links);
System.err.println ("proplinks: "+proplinks);
} }
} }