From fc1d8dfb26bd110f6af393861e90cc462889327b Mon Sep 17 00:00:00 2001 From: hns Date: Tue, 8 Sep 2009 19:48:08 +0000 Subject: [PATCH] Remove cache replication functionality including the underlying RMI networking code, make Node not serializable and Node.nmgr final, and resurrect TransientNode for use in sessions. --- .../framework/core/RemoteApplication.java | 69 -- src/helma/framework/core/Session.java | 2 +- src/helma/main/ApplicationManager.java | 28 +- src/helma/main/Server.java | 50 +- src/helma/main/ServerConfig.java | 13 - src/helma/objectmodel/TransientNode.java | 950 ++++++++++++++++++ src/helma/objectmodel/TransientProperty.java | 346 +++++++ src/helma/objectmodel/db/DbMapping.java | 12 - .../objectmodel/db/IReplicationListener.java | 33 - src/helma/objectmodel/db/Node.java | 104 +- src/helma/objectmodel/db/NodeManager.java | 84 +- src/helma/objectmodel/db/Replicator.java | 128 --- 12 files changed, 1324 insertions(+), 495 deletions(-) delete mode 100644 src/helma/framework/core/RemoteApplication.java create mode 100644 src/helma/objectmodel/TransientNode.java create mode 100644 src/helma/objectmodel/TransientProperty.java delete mode 100644 src/helma/objectmodel/db/IReplicationListener.java delete mode 100644 src/helma/objectmodel/db/Replicator.java diff --git a/src/helma/framework/core/RemoteApplication.java b/src/helma/framework/core/RemoteApplication.java deleted file mode 100644 index 10a7ec6d..00000000 --- a/src/helma/framework/core/RemoteApplication.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Helma License Notice - * - * The contents of this file are subject to the Helma License - * Version 2.0 (the "License"). You may not use this file except in - * compliance with the License. A copy of the License is available at - * http://adele.helma.org/download/helma/license.txt - * - * Copyright 1998-2003 Helma Software. All Rights Reserved. - * - * $RCSfile$ - * $Author$ - * $Revision$ - * $Date$ - */ - -package helma.framework.core; - -import helma.framework.*; -import helma.objectmodel.db.*; -import java.rmi.*; -import java.rmi.server.*; -import java.util.Vector; - -/** - * Proxy class for Aplication that listens to requests via RMI. - */ -public class RemoteApplication extends UnicastRemoteObject implements IRemoteApp, - IReplicationListener { - Application app; - - /** - * Creates a new RemoteApplication object. - * - * @param app ... - * - * @throws RemoteException ... - */ - public RemoteApplication(Application app) throws RemoteException { - this.app = app; - } - - /** - * ping method to let clients know if the server is reachable - */ - public void ping() { - // do nothing - } - - /** - * Execute a request coming in from a web client. - */ - public ResponseTrans execute(RequestTrans req) { - return app.execute(req); - } - - /** - * Update HopObjects in this application's cache. This is used to replicate - * application caches in a distributed app environment - */ - public void replicateCache(Vector add, Vector delete) { - if (!"true".equalsIgnoreCase(app.getProperty("allowReplication"))) { - app.logEvent("Rejecting cache replication event: allowReplication property is not set to true"); - throw new RuntimeException("Replication event rejected: setup does not allow replication."); - } - - app.nmgr.replicateCache(add, delete); - } -} diff --git a/src/helma/framework/core/Session.java b/src/helma/framework/core/Session.java index 4f720457..8bb84de4 100644 --- a/src/helma/framework/core/Session.java +++ b/src/helma/framework/core/Session.java @@ -67,7 +67,7 @@ public class Session implements Serializable { this.app = app; this.uid = null; this.userHandle = null; - cacheNode = new Node("session", null, app.getWrappedNodeManager()); + cacheNode = new TransientNode("session"); onSince = System.currentTimeMillis(); lastTouched = lastModified = onSince; } diff --git a/src/helma/main/ApplicationManager.java b/src/helma/main/ApplicationManager.java index 620cd8a6..5918e7ea 100644 --- a/src/helma/main/ApplicationManager.java +++ b/src/helma/main/ApplicationManager.java @@ -29,7 +29,6 @@ import org.mortbay.jetty.servlet.ServletHandler; import org.mortbay.jetty.servlet.ServletHolder; import java.io.*; -import java.rmi.*; import java.util.*; import helma.util.ResourceProperties; import helma.servlet.EmbeddedServletClient; @@ -41,7 +40,6 @@ public class ApplicationManager implements XmlRpcHandler { private Hashtable descriptors; private Hashtable applications; private Hashtable xmlrpcHandlers; - private int rmiPort; private ResourceProperties props; private Server server; private long lastModified; @@ -54,23 +52,9 @@ public class ApplicationManager implements XmlRpcHandler { * @param props the properties defining the running apps * @param server the server instance */ - public ApplicationManager(ResourceProperties props, - Server server) { - this(props, server, 0); - } - - /** - * Creates a new ApplicationManager object. - * - * @param props the properties defining the running apps - * @param server the server instance - * @param port The RMI port we're binding to - */ - public ApplicationManager(ResourceProperties props, - Server server, int port) { + public ApplicationManager(ResourceProperties props, Server server) { this.props = props; this.server = server; - this.rmiPort = port; descriptors = new Hashtable(); applications = new Hashtable(); xmlrpcHandlers = new Hashtable(); @@ -488,11 +472,6 @@ public class ApplicationManager implements XmlRpcHandler { try { getLogger().info("Binding application " + appName + " :: " + app.hashCode() + " :: " + this.hashCode()); - // bind to RMI server - if (rmiPort > 0) { - Naming.rebind("//:" + rmiPort + "/" + appName, new RemoteApplication(app)); - } - // set application URL prefix if it isn't set in app.properties if (!app.hasExplicitBaseURI()) { app.setBaseURI(mountpoint); @@ -594,11 +573,6 @@ public class ApplicationManager implements XmlRpcHandler { getLogger().info("Unbinding application " + appName); try { - // unbind from RMI server - if (rmiPort > 0) { - Naming.unbind("//:" + rmiPort + "/" + appName); - } - // unbind from Jetty HTTP server if (jetty != null) { if (appContext != null) { diff --git a/src/helma/main/Server.java b/src/helma/main/Server.java index b9b46a1c..91798664 100644 --- a/src/helma/main/Server.java +++ b/src/helma/main/Server.java @@ -59,7 +59,7 @@ public class Server implements Runnable { // server start time public final long starttime; - // if paranoid == true we only accept RMI and XML-RPC connections from + // if paranoid == true we only accept XML-RPC connections from // explicitly listed hosts. public boolean paranoid; private ApplicationManager appManager; @@ -205,14 +205,6 @@ public class Server implements Runnable { } } - if (!config.hasRmiPort() && sysProps.getProperty("rmiPort") != null) { - try { - config.setRmiPort(getInetSocketAddress(sysProps.getProperty("rmiPort"))); - } catch (Exception portx) { - throw new Exception("Error parsing RMI server port property from server.properties: " + portx); - } - } - if (!config.hasXmlrpcPort() && sysProps.getProperty("xmlrpcPort") != null) { try { config.setXmlrpcPort(getInetSocketAddress(sysProps.getProperty("xmlrpcPort"))); @@ -238,12 +230,6 @@ public class Server implements Runnable { config.setPropFile(new File(args[++i])); } else if (args[i].equals("-a") && ((i + 1) < args.length)) { config.setApps(StringUtils.split(args[++i])); - } else if (args[i].equals("-p") && ((i + 1) < args.length)) { - try { - config.setRmiPort(getInetSocketAddress(args[++i])); - } catch (Exception portx) { - throw new Exception("Error parsing RMI server port property: " + portx); - } } else if (args[i].equals("-x") && ((i + 1) < args.length)) { try { config.setXmlrpcPort(getInetSocketAddress(args[++i])); @@ -335,7 +321,6 @@ public class Server implements Runnable { System.out.println(" -w [ip:]port Specify embedded web server address/port"); System.out.println(" -x [ip:]port Specify XML-RPC address/port"); System.out.println(" -jk [ip:]port Specify AJP13 address/port"); - System.out.println(" -p [ip:]port Specify RMI address/port"); System.out.println(""); System.out.println("Supported formats for server ports:"); System.out.println(" "); @@ -359,10 +344,6 @@ public class Server implements Runnable { checkPort(config.getWebsrvPort()); } - if (config.hasRmiPort()) { - checkPort(config.getRmiPort()); - } - if (config.hasXmlrpcPort()) { checkPort(config.getXmlrpcPort()); } @@ -588,34 +569,7 @@ public class Server implements Runnable { logger.info("Starting XML-RPC server on port " + (xmlrpcPort)); } - if (config.hasRmiPort()) { - InetSocketAddress rmiPort = config.getRmiPort(); - if (paranoid) { - HelmaSocketFactory factory = new HelmaSocketFactory(); - String rallow = sysProps.getProperty("allowWeb"); - if (rallow == null) { - rallow = sysProps.getProperty("allowRMI"); - } - - if (rallow != null) { - StringTokenizer st = new StringTokenizer(rallow, " ,;"); - - while (st.hasMoreTokens()) - factory.addAddress(st.nextToken()); - } - - RMISocketFactory.setSocketFactory(factory); - } - - logger.info("Starting RMI server on port " + rmiPort); - LocateRegistry.createRegistry(rmiPort.getPort()); - - // create application manager which binds to the given RMI port - appManager = new ApplicationManager(appsProps, this, rmiPort.getPort()); - } else { - // create application manager without RMI port - appManager = new ApplicationManager(appsProps, this); - } + appManager = new ApplicationManager(appsProps, this); if (xmlrpc != null) { xmlrpc.addHandler("$default", appManager); diff --git a/src/helma/main/ServerConfig.java b/src/helma/main/ServerConfig.java index 68364cca..6ce25592 100644 --- a/src/helma/main/ServerConfig.java +++ b/src/helma/main/ServerConfig.java @@ -25,7 +25,6 @@ import java.net.InetSocketAddress; public class ServerConfig { - private InetSocketAddress rmiPort = null; private InetSocketAddress xmlrpcPort = null; private InetSocketAddress websrvPort = null; private InetSocketAddress ajp13Port = null; @@ -46,10 +45,6 @@ public class ServerConfig { return (homeDir != null); } - public boolean hasRmiPort() { - return (rmiPort != null); - } - public boolean hasXmlrpcPort() { return (xmlrpcPort != null); } @@ -66,14 +61,6 @@ public class ServerConfig { return (apps != null); } - public InetSocketAddress getRmiPort() { - return rmiPort; - } - - public void setRmiPort(InetSocketAddress rmiPort) { - this.rmiPort = rmiPort; - } - public InetSocketAddress getXmlrpcPort() { return xmlrpcPort; } diff --git a/src/helma/objectmodel/TransientNode.java b/src/helma/objectmodel/TransientNode.java new file mode 100644 index 00000000..d9c5df63 --- /dev/null +++ b/src/helma/objectmodel/TransientNode.java @@ -0,0 +1,950 @@ +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + +package helma.objectmodel; + +import helma.framework.IPathElement; +import helma.objectmodel.db.DbMapping; +import helma.objectmodel.db.Relation; +import helma.objectmodel.db.Node; +import helma.util.*; +import java.io.*; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * A transient implementation of INode. An instance of this class can't be + * made persistent by reachability from a persistent node. To make a persistent-capable + * object, class helma.objectmodel.db.Node has to be used. + */ +public class TransientNode implements INode, Serializable { + private static long idgen = 0; + protected Hashtable propMap; + protected Hashtable nodeMap; + protected Vector nodes; + protected TransientNode parent; + protected Vector links; // links to this node + protected Vector proplinks; // nodes using this node as property + transient String prototype; + protected long created; + protected long lastmodified; + protected String id; + protected String name; + + // is the main identity a named property or an anonymous node in a collection? + protected boolean anonymous = false; + transient DbMapping dbmap; + INode cacheNode; + + /** + * Creates a new TransientNode object. + */ + public TransientNode() { + id = generateID(); + name = id; + created = lastmodified = System.currentTimeMillis(); + } + + /** + * Make a new TransientNode object with a given name + */ + public TransientNode(String n) { + id = generateID(); + name = ((n == null) || "".equals(n)) ? id : n; + created = lastmodified = System.currentTimeMillis(); + } + + /** + * + * + * @return ... + */ + public static String generateID() { + // make transient ids differ from persistent ones + // and are unique within on runtime session + return "t" + idgen++; + } + + /** + * + * + * @param dbmap ... + */ + public void setDbMapping(DbMapping dbmap) { + this.dbmap = dbmap; + } + + /** + * + * + * @return ... + */ + public DbMapping getDbMapping() { + return dbmap; + } + + /** + * navigation-related + */ + public String getID() { + return id; + } + + /** + * + * + * @return ... + */ + public boolean isAnonymous() { + return anonymous; + } + + /** + * + * + * @return ... + */ + public String getName() { + return name; + } + + /** + * + * + * @return ... + */ + public String getElementName() { + return anonymous ? id : name; + } + + /** + * + * + * @return ... + */ + public int getState() { + return TRANSIENT; + } + + /** + * + * + * @param s ... + */ + public void setState(int s) { + // state always is TRANSIENT on this kind of node + } + + /** + * + * + * @return ... + */ + public String getFullName() { + return getFullName(null); + } + + /** + * + * + * @param root ... + * + * @return ... + */ + public String getFullName(INode root) { + String divider = null; + StringBuffer b = new StringBuffer(); + TransientNode p = this; + + while ((p != null) && (p.parent != null) && (p != root)) { + if (divider != null) { + b.insert(0, divider); + } else { + divider = "/"; + } + + b.insert(0, p.getElementName()); + p = p.parent; + } + + return b.toString(); + } + + /** + * + * + * @param name ... + */ + public void setName(String name) { + // if (name.indexOf('/') > -1) + // throw new RuntimeException ("The name of the node must not contain \"/\"."); + if ((name == null) || (name.trim().length() == 0)) { + this.name = id; + } else { + this.name = name; + } + } + + /** + * + * + * @return ... + */ + public String getPrototype() { + // if prototype is null, it's a vanilla HopObject. + if (prototype == null) { + return "hopobject"; + } + + return prototype; + } + + /** + * + * + * @param proto ... + */ + public void setPrototype(String proto) { + this.prototype = proto; + } + + /** + * + * + * @return ... + */ + public INode getParent() { + return parent; + } + + /** + * INode-related + */ + public void setSubnodeRelation(String rel) { + throw new RuntimeException("Can't set subnode relation for non-persistent Node."); + } + + /** + * + * + * @return ... + */ + public String getSubnodeRelation() { + return null; + } + + /** + * + * + * @return ... + */ + public int numberOfNodes() { + return (nodes == null) ? 0 : nodes.size(); + } + + /** + * + * + * @param elem ... + * + * @return ... + */ + public INode addNode(INode elem) { + return addNode(elem, numberOfNodes()); + } + + /** + * + * + * @param elem ... + * @param where ... + * + * @return ... + */ + public INode addNode(INode elem, int where) { + if ((where < 0) || (where > numberOfNodes())) { + where = numberOfNodes(); + } + + String n = elem.getName(); + + if (n.indexOf('/') > -1) { + throw new RuntimeException("The name of a node must not contain \"/\" (slash)."); + } + + // IServer.getLogger().log ("adding: "+node+" -- "+node.getContentLength ()); + if ((nodeMap != null) && (nodeMap.get(elem.getID()) != null)) { + nodes.removeElement(elem); + where = Math.min(where, numberOfNodes()); + nodes.insertElementAt(elem, where); + + return elem; + } + + if (nodeMap == null) { + nodeMap = new Hashtable(); + } + + if (nodes == null) { + nodes = new Vector(); + } + + nodeMap.put(elem.getID(), elem); + nodes.insertElementAt(elem, where); + + if (elem instanceof TransientNode) { + TransientNode node = (TransientNode) elem; + + if (node.parent == null) { + node.parent = this; + node.anonymous = true; + } + } + + lastmodified = System.currentTimeMillis(); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, node)); + return elem; + } + + /** + * + * + * @return ... + */ + public INode createNode() { + return createNode(null, 0); // where is ignored since this is an anonymous node + } + + /** + * + * + * @param where ... + * + * @return ... + */ + public INode createNode(int where) { + return createNode(null, where); + } + + /** + * + * + * @param nm ... + * + * @return ... + */ + public INode createNode(String nm) { + return createNode(nm, numberOfNodes()); // where is usually ignored (if nm != null) + } + + /** + * + * + * @param nm ... + * @param where ... + * + * @return ... + */ + public INode createNode(String nm, int where) { + boolean anon = false; + + if ((nm == null) || "".equals(nm.trim())) { + anon = true; + } + + INode n = new TransientNode(nm); + + if (anon) { + addNode(n, where); + } else { + setNode(nm, n); + } + + return n; + } + + /** + * register a node that links to this node. + */ + + /* protected void registerLink (TransientNode from) { + if (links == null) + links = new Vector (); + if (!links.contains (from)) + links.addElement (from); + } */ + public IPathElement getParentElement() { + return getParent(); + } + + /** + * + * + * @param name ... + * + * @return ... + */ + public IPathElement getChildElement(String name) { + return getNode(name); + } + + /** + * + * + * @param name ... + * + * @return ... + */ + public INode getSubnode(String name) { + StringTokenizer st = new StringTokenizer(name, "/"); + TransientNode retval = this; + TransientNode runner; + + while (st.hasMoreTokens() && (retval != null)) { + runner = retval; + + String next = st.nextToken().trim().toLowerCase(); + + if ("".equals(next)) { + retval = this; + } else { + retval = (runner.nodeMap == null) ? null + : (TransientNode) runner.nodeMap.get(next); + } + + if (retval == null) { + retval = (TransientNode) runner.getNode(next); + } + } + + return retval; + } + + /** + * + * + * @param index ... + * + * @return ... + */ + public INode getSubnodeAt(int index) { + return (nodes == null) ? null : (INode) nodes.elementAt(index); + } + + /** + * + * + * @param n ... + * + * @return ... + */ + public int contains(INode n) { + if ((n == null) || (nodes == null)) { + return -1; + } + + return nodes.indexOf(n); + } + + /** + * + * + * @return ... + */ + public boolean remove() { + if (anonymous) { + parent.unset(name); + } else { + parent.removeNode(this); + } + + return true; + } + + /** + * + * + * @param node ... + */ + public void removeNode(INode node) { + // IServer.getLogger().log ("removing: "+ node); + releaseNode(node); + + TransientNode n = (TransientNode) node; + + if ((n.getParent() == this) && n.anonymous) { + int l = (n.links == null) ? 0 : n.links.size(); // notify nodes that link to n that n is going down. + + for (int i = 0; i < l; i++) { + TransientNode link = (TransientNode) n.links.elementAt(i); + + link.releaseNode(n); + } + + if (n.proplinks != null) { + // clean up all nodes that use n as a property + for (Enumeration e1 = n.proplinks.elements(); e1.hasMoreElements();) + try { + TransientProperty p = (TransientProperty) e1.nextElement(); + + p.node.propMap.remove(p.propname.toLowerCase()); + } catch (Exception ignore) { + } + } + + // remove all subnodes, giving them a chance to destroy themselves. + Vector v = new Vector(); // removeElement modifies the Vector we are enumerating, so we are extra careful. + + for (Enumeration e3 = n.getSubnodes(); e3.hasMoreElements();) { + v.addElement(e3.nextElement()); + } + + int m = v.size(); + + for (int i = 0; i < m; i++) { + n.removeNode((TransientNode) v.elementAt(i)); + } + } else { + // + n.links.removeElement(this); + } + } + + /** + * "Physically" remove a subnode from the subnodes table. + * the logical stuff necessary for keeping data consistent is done elsewhere (in removeNode). + */ + protected void releaseNode(INode node) { + if ((nodes == null) || (nodeMap == null)) { + + return; + } + + int runner = nodes.indexOf(node); + + // this is due to difference between .equals() and == + while ((runner > -1) && (nodes.elementAt(runner) != node)) + runner = nodes.indexOf(node, Math.min(nodes.size() - 1, runner + 1)); + + if (runner > -1) { + nodes.removeElementAt(runner); + } + + // nodes.remove (node); + nodeMap.remove(node.getName().toLowerCase()); + + // Server.throwNodeEvent (new NodeEvent (node, NodeEvent.NODE_REMOVED)); + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_REMOVED, node)); + lastmodified = System.currentTimeMillis(); + + // IServer.getLogger().log ("released node "+node +" from "+this+" oldobj = "+what); + } + + /** + * + * + * @return ... + */ + public Enumeration getSubnodes() { + return (nodes == null) ? new Vector().elements() : nodes.elements(); + } + + /** + * property-related + */ + public Enumeration properties() { + return (propMap == null) ? new EmptyEnumeration() : propMap.keys(); + } + + private TransientProperty getProperty(String propname) { + TransientProperty prop = (propMap == null) ? null : (TransientProperty) propMap.get(propname); + + // check if we have to create a virtual node + if ((prop == null) && (dbmap != null)) { + Relation rel = dbmap.getPropertyRelation(propname); + + if ((rel != null) && rel.isVirtual()) { + prop = makeVirtualNode(propname, rel); + } + } + + return prop; + } + + private TransientProperty makeVirtualNode(String propname, Relation rel) { + INode node = new Node(rel.getPropName(), rel.getPrototype(), + dbmap.getWrappedNodeManager()); + + // node.setState (TRANSIENT); + // make a db mapping good enough that the virtual node finds its subnodes + // DbMapping dbm = new DbMapping (); + // dbm.setSubnodeRelation (rel); + // dbm.setPropertyRelation (rel); + node.setDbMapping(rel.getVirtualMapping()); + setNode(propname, node); + + return (TransientProperty) propMap.get(propname); + } + + /** + * + * + * @param propname ... + * + * @return ... + */ + public IProperty get(String propname) { + return getProperty(propname); + } + + /** + * + * + * @param propname ... + * @param defaultValue ... + * + * @return ... + */ + public String getString(String propname, String defaultValue) { + String propValue = getString(propname); + + return (propValue == null) ? defaultValue : propValue; + } + + /** + * + * + * @param propname ... + * + * @return ... + */ + public String getString(String propname) { + TransientProperty prop = getProperty(propname); + + try { + return prop.getStringValue(); + } catch (Exception ignore) { + } + + return null; + } + + /** + * + * + * @param propname ... + * + * @return ... + */ + public long getInteger(String propname) { + TransientProperty prop = getProperty(propname); + + try { + return prop.getIntegerValue(); + } catch (Exception ignore) { + } + + return 0; + } + + /** + * + * + * @param propname ... + * + * @return ... + */ + public double getFloat(String propname) { + TransientProperty prop = getProperty(propname); + + try { + return prop.getFloatValue(); + } catch (Exception ignore) { + } + + return 0.0; + } + + /** + * + * + * @param propname ... + * + * @return ... + */ + public Date getDate(String propname) { + TransientProperty prop = getProperty(propname); + + try { + return prop.getDateValue(); + } catch (Exception ignore) { + } + + return null; + } + + /** + * + * + * @param propname ... + * + * @return ... + */ + public boolean getBoolean(String propname) { + TransientProperty prop = getProperty(propname); + + try { + return prop.getBooleanValue(); + } catch (Exception ignore) { + } + + return false; + } + + /** + * + * + * @param propname ... + * + * @return ... + */ + public INode getNode(String propname) { + TransientProperty prop = getProperty(propname); + + try { + return prop.getNodeValue(); + } catch (Exception ignore) { + } + + return null; + } + + /** + * + * + * @param propname ... + * + * @return ... + */ + public Object getJavaObject(String propname) { + TransientProperty prop = getProperty(propname); + + try { + return prop.getJavaObjectValue(); + } catch (Exception ignore) { + } + + return null; + } + + // create a property if it doesn't exist for this name + private TransientProperty initProperty(String propname) { + if (propMap == null) { + propMap = new Hashtable(); + } + + propname = propname.trim(); + TransientProperty prop = (TransientProperty) propMap.get(propname); + + if (prop == null) { + prop = new TransientProperty(propname, this); + propMap.put(propname, prop); + } + + return prop; + } + + /** + * + * + * @param propname ... + * @param value ... + */ + public void setString(String propname, String value) { + // IServer.getLogger().log ("setting String prop"); + TransientProperty prop = initProperty(propname); + + prop.setStringValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + } + + /** + * + * + * @param propname ... + * @param value ... + */ + public void setInteger(String propname, long value) { + // IServer.getLogger().log ("setting bool prop"); + TransientProperty prop = initProperty(propname); + + prop.setIntegerValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + } + + /** + * + * + * @param propname ... + * @param value ... + */ + public void setFloat(String propname, double value) { + // IServer.getLogger().log ("setting bool prop"); + TransientProperty prop = initProperty(propname); + + prop.setFloatValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + } + + /** + * + * + * @param propname ... + * @param value ... + */ + public void setBoolean(String propname, boolean value) { + // IServer.getLogger().log ("setting bool prop"); + TransientProperty prop = initProperty(propname); + + prop.setBooleanValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + } + + /** + * + * + * @param propname ... + * @param value ... + */ + public void setDate(String propname, Date value) { + // IServer.getLogger().log ("setting date prop"); + TransientProperty prop = initProperty(propname); + + prop.setDateValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + } + + /** + * + * + * @param propname ... + * @param value ... + */ + public void setJavaObject(String propname, Object value) { + // IServer.getLogger().log ("setting date prop"); + TransientProperty prop = initProperty(propname); + + prop.setJavaObjectValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + } + + /** + * + * + * @param propname ... + * @param value ... + */ + public void setNode(String propname, INode value) { + // IServer.getLogger().log ("setting date prop"); + TransientProperty prop = initProperty(propname); + + prop.setNodeValue(value); + + // check if the main identity of this node is as a named property + // or as an anonymous node in a collection + if (value instanceof TransientNode) { + TransientNode n = (TransientNode) value; + + if (n.parent == null) { + n.name = propname; + n.parent = this; + n.anonymous = false; + } + } + + lastmodified = System.currentTimeMillis(); + } + + /** + * + * + * @param propname ... + */ + public void unset(String propname) { + if (propMap != null && propname != null) { + propMap.remove(propname); + lastmodified = System.currentTimeMillis(); + } + } + + public long lastModified() { + return lastmodified; + } + + /** + * + * + * @return ... + */ + public long created() { + return created; + } + + /** + * + * + * @return ... + */ + public String toString() { + return "TransientNode " + name; + } + + /** + * Get the cache node for this node. This can + * be used to store transient cache data per node + * from Javascript. + */ + public synchronized INode getCacheNode() { + if (cacheNode == null) { + cacheNode = new TransientNode(); + } + + return cacheNode; + } + + /** + * Reset the cache node for this node. + */ + public synchronized void clearCacheNode() { + cacheNode = null; + } +} diff --git a/src/helma/objectmodel/TransientProperty.java b/src/helma/objectmodel/TransientProperty.java new file mode 100644 index 00000000..d6013a6c --- /dev/null +++ b/src/helma/objectmodel/TransientProperty.java @@ -0,0 +1,346 @@ +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + +package helma.objectmodel; + +import java.io.*; +import java.text.*; +import java.util.Date; + +/** + * A property implementation for Nodes stored inside a database. + */ +public final class TransientProperty implements IProperty, Serializable { + protected String propname; + protected TransientNode node; + public String svalue; + public boolean bvalue; + public long lvalue; + public double dvalue; + public INode nvalue; + public Object jvalue; + public int type; + + /** + * Creates a new Property object. + * + * @param node ... + */ + public TransientProperty(TransientNode node) { + this.node = node; + } + + /** + * Creates a new Property object. + * + * @param propname ... + * @param node ... + */ + public TransientProperty(String propname, TransientNode node) { + this.propname = propname; + this.node = node; + } + + /** + * + * + * @return ... + */ + public String getName() { + return propname; + } + + /** + * + * + * @return ... + */ + public Object getValue() { + switch (type) { + case STRING: + return svalue; + + case BOOLEAN: + return new Boolean(bvalue); + + case INTEGER: + return new Long(lvalue); + + case FLOAT: + return new Double(dvalue); + + case DATE: + return new Date(lvalue); + + case NODE: + return nvalue; + + case JAVAOBJECT: + return jvalue; + } + + return null; + } + + /** + * + * + * @param value ... + */ + public void setStringValue(String value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = STRING; + this.svalue = value; + } + + /** + * + * + * @param value ... + */ + public void setIntegerValue(long value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = INTEGER; + this.lvalue = value; + } + + /** + * + * + * @param value ... + */ + public void setFloatValue(double value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = FLOAT; + this.dvalue = value; + } + + /** + * + * + * @param value ... + */ + public void setDateValue(Date value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = DATE; + this.lvalue = value.getTime(); + } + + /** + * + * + * @param value ... + */ + public void setBooleanValue(boolean value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = BOOLEAN; + this.bvalue = value; + } + + /** + * + * + * @param value ... + */ + public void setNodeValue(INode value) { + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = NODE; + this.nvalue = value; + } + + /** + * + * + * @param value ... + */ + public void setJavaObjectValue(Object value) { + if (type == NODE) { + this.nvalue = null; + } + + type = JAVAOBJECT; + this.jvalue = value; + } + + /** + * + * + * @return ... + */ + public String getStringValue() { + switch (type) { + case STRING: + return svalue; + + case BOOLEAN: + return "" + bvalue; + + case DATE: + + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + return format.format(new Date(lvalue)); + + case INTEGER: + return Long.toString(lvalue); + + case FLOAT: + return Double.toString(dvalue); + + case NODE: + return nvalue.getName(); + + case JAVAOBJECT: + return (jvalue == null) ? null : jvalue.toString(); + } + + return ""; + } + + /** + * + * + * @return ... + */ + public String toString() { + return getStringValue(); + } + + /** + * + * + * @return ... + */ + public long getIntegerValue() { + if (type == INTEGER) { + return lvalue; + } + + return 0; + } + + /** + * + * + * @return ... + */ + public double getFloatValue() { + if (type == FLOAT) { + return dvalue; + } + + return 0.0; + } + + /** + * + * + * @return ... + */ + public Date getDateValue() { + if (type == DATE) { + return new Date(lvalue); + } + + return null; + } + + /** + * + * + * @return ... + */ + public boolean getBooleanValue() { + if (type == BOOLEAN) { + return bvalue; + } + + return false; + } + + /** + * + * + * @return ... + */ + public INode getNodeValue() { + if (type == NODE) { + return nvalue; + } + + return null; + } + + /** + * + * + * @return ... + */ + public Object getJavaObjectValue() { + if (type == JAVAOBJECT) { + return jvalue; + } + + return null; + } + + /** + * + * + * @return ... + */ + public int getType() { + return type; + } +} diff --git a/src/helma/objectmodel/db/DbMapping.java b/src/helma/objectmodel/db/DbMapping.java index 90306657..914079de 100644 --- a/src/helma/objectmodel/db/DbMapping.java +++ b/src/helma/objectmodel/db/DbMapping.java @@ -117,9 +117,6 @@ public final class DbMapping { // timestamp of last modification of an object of this type long lastDataChange = 0; - // evict objects of this type when received via replication - private boolean evictOnReplication; - // Set of mappings that depend on us and should be forwarded last data change events HashSet dependentMappings = new HashSet(); @@ -229,7 +226,6 @@ public final class DbMapping { idField = props.getProperty("_id"); nameField = props.getProperty("_name"); protoField = props.getProperty("_prototype"); - evictOnReplication = "true".equals(props.getProperty("_evictOnReplication")); parentSetting = props.getProperty("_parent"); if (parentSetting != null) { @@ -594,14 +590,6 @@ public final class DbMapping { return protoField; } - /** - * Should objects of this type be evicted/discarded/reloaded when received via - * cache replication? - */ - public boolean evictOnReplication() { - return evictOnReplication; - } - /** * Translate a database column name to an object property name according to this mapping. */ diff --git a/src/helma/objectmodel/db/IReplicationListener.java b/src/helma/objectmodel/db/IReplicationListener.java deleted file mode 100644 index 6bf3e191..00000000 --- a/src/helma/objectmodel/db/IReplicationListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Helma License Notice - * - * The contents of this file are subject to the Helma License - * Version 2.0 (the "License"). You may not use this file except in - * compliance with the License. A copy of the License is available at - * http://adele.helma.org/download/helma/license.txt - * - * Copyright 1998-2003 Helma Software. All Rights Reserved. - * - * $RCSfile$ - * $Author$ - * $Revision$ - * $Date$ - */ - -package helma.objectmodel.db; - -import java.rmi.Remote; -import java.rmi.RemoteException; -import java.util.Vector; - -/** - * RMI interface for an application. Currently only execute is used and supported. - */ -public interface IReplicationListener extends Remote { - /** - * Update HopObjects in this application's cache. This is used to replicate - * application caches in a distributed app environment - */ - public void replicateCache(Vector add, Vector delete) - throws RemoteException; -} diff --git a/src/helma/objectmodel/db/Node.java b/src/helma/objectmodel/db/Node.java index 8a807f6b..d87b8dff 100644 --- a/src/helma/objectmodel/db/Node.java +++ b/src/helma/objectmodel/db/Node.java @@ -34,8 +34,7 @@ import java.util.*; * An implementation of INode that can be stored in the internal database or * an external relational database. */ -public final class Node implements INode, Serializable { - static final long serialVersionUID = -3740339688506633675L; +public final class Node implements INode { // The handle to the node's parent protected NodeHandle parentHandle; @@ -60,7 +59,7 @@ public final class Node implements INode, Serializable { private transient String prototype; private transient NodeHandle handle; private transient INode cacheNode; - transient volatile WrappedNodeManager nmgr; + transient final WrappedNodeManager nmgr; transient DbMapping dbmap; transient Key primaryKey = null; transient String subnodeRelation = null; @@ -70,14 +69,6 @@ public final class Node implements INode, Serializable { transient private volatile int state; private static long idgen = 0; - /** - * Creates an empty, uninitialized Node. The init() method must be called on the - * Node before it can do anything useful. - */ - protected Node() { - created = lastmodified = System.currentTimeMillis(); - } - /** * Creates an empty, uninitialized Node with the given create and modify time. * This is used for null-node references in the node cache. @@ -85,6 +76,19 @@ public final class Node implements INode, Serializable { */ protected Node(long timestamp) { created = lastmodified = timestamp; + this.nmgr = null; + } + + /** + * Creates an empty, uninitialized Node. The init() method must be called on the + * Node before it can do anything useful. + */ + protected Node(WrappedNodeManager nmgr) { + if (nmgr == null) { + throw new NullPointerException("nmgr"); + } + this.nmgr = nmgr; + created = lastmodified = System.currentTimeMillis(); } /** @@ -93,10 +97,14 @@ public final class Node implements INode, Serializable { * Also used by embedded database to re-create an existing Node. */ public Node(String name, String id, String prototype, WrappedNodeManager nmgr) { + if (nmgr == null) { + throw new NullPointerException("nmgr"); + } + this.nmgr = nmgr; if (prototype == null) { prototype = "HopObject"; } - init(nmgr.getDbMapping(prototype), id, name, prototype, null, nmgr); + init(nmgr.getDbMapping(prototype), id, name, prototype, null); } /** @@ -162,12 +170,8 @@ public final class Node implements INode, Serializable { /** * Initializer used for nodes being instanced from an embedded or relational database. */ - public synchronized void init(DbMapping dbm, String id, String name, String prototype, - Hashtable propMap, WrappedNodeManager nmgr) { - if (nmgr == null) { - throw new NullPointerException("nmgr"); - } - this.nmgr = nmgr; + public synchronized void init(DbMapping dbm, String id, String name, + String prototype, Hashtable propMap) { this.dbmap = dbm; this.prototype = prototype; this.id = id; @@ -187,67 +191,6 @@ public final class Node implements INode, Serializable { } } - /** - * Read this object instance from a stream. This does some smart conversion to - * update from previous serialization formats. - */ - private void readObject(ObjectInputStream in) throws IOException { - try { - // as a general rule of thumb, if a string can be null use read/writeObject, - // if not it's save to use read/writeUTF. - // version indicates the serialization version - version = in.readShort(); - - if (version < 9) { - throw new IOException("Can't read pre 1.3.0 HopObject"); - } - - id = (String) in.readObject(); - name = (String) in.readObject(); - state = in.readInt(); - parentHandle = (NodeHandle) in.readObject(); - created = in.readLong(); - lastmodified = in.readLong(); - - subnodes = (SubnodeList) in.readObject(); - // left-over from links vector - in.readObject(); - propMap = (Hashtable) in.readObject(); - anonymous = in.readBoolean(); - prototype = (String) in.readObject(); - - } catch (ClassNotFoundException x) { - throw new IOException(x.toString()); - } - } - - /** - * Write out this instance to a stream - */ - private void writeObject(ObjectOutputStream out) throws IOException { - out.writeShort(9); // serialization version - out.writeObject(id); - out.writeObject(name); - out.writeInt(state); - out.writeObject(parentHandle); - out.writeLong(created); - out.writeLong(lastmodified); - - DbMapping smap = (dbmap == null) ? null : dbmap.getSubnodeMapping(); - - if (smap != null && smap.isRelational()) { - out.writeObject(null); - } else { - out.writeObject(subnodes); - } - - // left-over from links vector - out.writeObject(null); - out.writeObject(propMap); - out.writeBoolean(anonymous); - out.writeObject(prototype); - } - /** * used by Xml deserialization */ @@ -1710,9 +1653,6 @@ public final class Node implements INode, Serializable { 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; } } } diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index f7a0322f..15ef6d97 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -42,7 +42,6 @@ public final class NodeManager { protected IDGenerator idgen; private boolean logSql; private Log sqlLog = null; - protected boolean logReplication; private ArrayList listeners = new ArrayList(); // a wrapper that catches some Exceptions while accessing this NM @@ -77,19 +76,6 @@ public final class NodeManager { } logSql = "true".equalsIgnoreCase(props.getProperty("logsql")); - logReplication = "true".equalsIgnoreCase(props.getProperty("logReplication")); - - String replicationUrl = props.getProperty("replicationUrl"); - - if (replicationUrl != null) { - if (logReplication) { - app.logEvent("Setting up replication listener at " + replicationUrl); - } - - Replicator replicator = new Replicator(this); - replicator.addUrl(replicationUrl); - addNodeChangeListener(replicator); - } db = new XmlDatabase(); db.init(dbHome, app); @@ -123,7 +109,6 @@ public final class NodeManager { // notify the cache about the properties update cache.updateProperties(props); logSql = "true".equalsIgnoreCase(props.getProperty("logsql")); - logReplication = "true".equalsIgnoreCase(props.getProperty("logReplication")); } /** @@ -1311,8 +1296,6 @@ public final class NodeManager { if ((dbm == null) || !dbm.isRelational()) { node = (Node) db.getNode(txn, kstr); - node.nmgr = safe; - if ((node != null) && (dbm != null)) { node.setDbMapping(dbm); } @@ -1388,15 +1371,11 @@ public final class NodeManager { if (node == null && (dbm == null || !dbm.isRelational())) { node = (Node) db.getNode(txn, kstr); - node.nmgr = safe; } - return node; } else if (rel == null || dbm == null || !dbm.isRelational()) { node = (Node) db.getNode(txn, kstr); - node.nmgr = safe; node.setDbMapping(dbm); - return node; } else { Statement stmt = null; @@ -1476,7 +1455,7 @@ public final class NodeManager { String protoName = dbm.getTypeName(); DbMapping dbmap = dbm; - Node node = new Node(); + Node node = new Node(safe); for (int i = 0; i < columns.length; i++) { @@ -1683,7 +1662,7 @@ public final class NodeManager { } } - node.init(dbmap, id, name, protoName, propMap, safe); + node.init(dbmap, id, name, protoName, propMap); return node; } @@ -1776,65 +1755,6 @@ public final class NodeManager { } } - - /** - * Receive notification from a remote app that objects in its cache have been - * modified. - */ - public void replicateCache(Vector add, Vector delete) { - if (logReplication) { - app.logEvent("Received cache replication event: " + add.size() + " added, " + - delete.size() + " deleted"); - } - - synchronized (cache) { - // long now = System.currentTimeMillis(); - - for (Enumeration en = add.elements(); en.hasMoreElements();) { - Node n = (Node) en.nextElement(); - DbMapping dbm = app.getDbMapping(n.getPrototype()); - - if (dbm != null) { - dbm.setLastDataChange(); - } - - n.setDbMapping(dbm); - n.nmgr = safe; - - if (dbm != null && dbm.evictOnReplication()) { - Node oldNode = (Node) cache.get(n.getKey()); - - if (oldNode != null) { - evictNode(oldNode); - } - } else { - n.lastParentSet = -1; - cache.put(n.getKey(), n); - } - } - - for (Enumeration en = delete.elements(); en.hasMoreElements();) { - // NOTE: it would be more efficient to transfer just the keys - // of nodes that are to be deleted. - Node n = (Node) en.nextElement(); - DbMapping dbm = app.getDbMapping(n.getPrototype()); - - if (dbm != null) { - dbm.setLastDataChange(); - } - - n.setDbMapping(dbm); - n.nmgr = safe; - - Node oldNode = (Node) cache.get(n.getKey()); - - if (oldNode != null) { - evictNode(oldNode); - } - } - } - } - private void setStatementValue(PreparedStatement stmt, int columnNumber, String value, DbColumn col) throws SQLException { if (value == null) { diff --git a/src/helma/objectmodel/db/Replicator.java b/src/helma/objectmodel/db/Replicator.java deleted file mode 100644 index f58f2047..00000000 --- a/src/helma/objectmodel/db/Replicator.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Helma License Notice - * - * The contents of this file are subject to the Helma License - * Version 2.0 (the "License"). You may not use this file except in - * compliance with the License. A copy of the License is available at - * http://adele.helma.org/download/helma/license.txt - * - * Copyright 1998-2003 Helma Software. All Rights Reserved. - * - * $RCSfile$ - * $Author$ - * $Revision$ - * $Date$ - */ - -package helma.objectmodel.db; - -import java.rmi.Naming; -import java.util.Vector; -import java.util.List; - -/** - * This class replicates the updates of transactions to other applications via RMI - */ -public class Replicator implements Runnable, NodeChangeListener { - Vector urls; - Vector add; - Vector delete; - Vector currentAdd; - Vector currentDelete; - Thread runner; - NodeManager nmgr; - - /** - * Creates a new Replicator object. - * - * @param nmgr ... - */ - public Replicator(NodeManager nmgr) { - urls = new Vector(); - add = new Vector(); - delete = new Vector(); - this.nmgr = nmgr; - runner = new Thread(this); - runner.start(); - } - - /** - * - * - * @param url ... - */ - public void addUrl(String url) { - urls.addElement(url); - - if (nmgr.logReplication) { - nmgr.app.logEvent("Adding replication listener: " + url); - } - } - - /** - * - */ - public void run() { - while (Thread.currentThread() == runner) { - if (prepareReplication()) { - for (int i = 0; i < urls.size(); i++) { - try { - String url = (String) urls.elementAt(i); - IReplicationListener listener = (IReplicationListener) Naming.lookup(url); - - if (listener == null) { - throw new NullPointerException("Replication listener not bound for URL "+url); - } - - listener.replicateCache(currentAdd, currentDelete); - - if (nmgr.logReplication) { - nmgr.app.logEvent("Sent cache replication event: " + - currentAdd.size() + " added, " + currentDelete.size() + - " deleted"); - } - } catch (Exception x) { - nmgr.app.logEvent("Error sending cache replication event: " + x); - if (nmgr.app.debug()) { - x.printStackTrace(); - } - } - } - } - - try { - if (runner != null) { - Thread.sleep(1000L); - } - } catch (InterruptedException ir) { - runner = null; - } - } - } - - - /** - * Called when a transaction is committed that has created, modified, - * deleted or changed the child collection one or more nodes. - */ - public synchronized void nodesChanged(List inserted, List updated, - List deleted, List parents) { - add.addAll(inserted); - add.addAll(updated); - delete.addAll(deleted); - } - - - private synchronized boolean prepareReplication() { - if ((add.size() == 0) && (delete.size() == 0)) { - return false; - } - - currentAdd = add; - currentDelete = delete; - add = new Vector(); - delete = new Vector(); - - return true; - } -}