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.

This commit is contained in:
hns 2009-09-08 19:48:08 +00:00
parent e1354889ec
commit fc1d8dfb26
12 changed files with 1324 additions and 495 deletions

View file

@ -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);
}
}

View file

@ -67,7 +67,7 @@ public class Session implements Serializable {
this.app = app; this.app = app;
this.uid = null; this.uid = null;
this.userHandle = null; this.userHandle = null;
cacheNode = new Node("session", null, app.getWrappedNodeManager()); cacheNode = new TransientNode("session");
onSince = System.currentTimeMillis(); onSince = System.currentTimeMillis();
lastTouched = lastModified = onSince; lastTouched = lastModified = onSince;
} }

View file

@ -29,7 +29,6 @@ import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder; import org.mortbay.jetty.servlet.ServletHolder;
import java.io.*; import java.io.*;
import java.rmi.*;
import java.util.*; import java.util.*;
import helma.util.ResourceProperties; import helma.util.ResourceProperties;
import helma.servlet.EmbeddedServletClient; import helma.servlet.EmbeddedServletClient;
@ -41,7 +40,6 @@ public class ApplicationManager implements XmlRpcHandler {
private Hashtable descriptors; private Hashtable descriptors;
private Hashtable applications; private Hashtable applications;
private Hashtable xmlrpcHandlers; private Hashtable xmlrpcHandlers;
private int rmiPort;
private ResourceProperties props; private ResourceProperties props;
private Server server; private Server server;
private long lastModified; private long lastModified;
@ -54,23 +52,9 @@ public class ApplicationManager implements XmlRpcHandler {
* @param props the properties defining the running apps * @param props the properties defining the running apps
* @param server the server instance * @param server the server instance
*/ */
public ApplicationManager(ResourceProperties props, public ApplicationManager(ResourceProperties props, Server server) {
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) {
this.props = props; this.props = props;
this.server = server; this.server = server;
this.rmiPort = port;
descriptors = new Hashtable(); descriptors = new Hashtable();
applications = new Hashtable(); applications = new Hashtable();
xmlrpcHandlers = new Hashtable(); xmlrpcHandlers = new Hashtable();
@ -488,11 +472,6 @@ public class ApplicationManager implements XmlRpcHandler {
try { try {
getLogger().info("Binding application " + appName + " :: " + app.hashCode() + " :: " + this.hashCode()); 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 // set application URL prefix if it isn't set in app.properties
if (!app.hasExplicitBaseURI()) { if (!app.hasExplicitBaseURI()) {
app.setBaseURI(mountpoint); app.setBaseURI(mountpoint);
@ -594,11 +573,6 @@ public class ApplicationManager implements XmlRpcHandler {
getLogger().info("Unbinding application " + appName); getLogger().info("Unbinding application " + appName);
try { try {
// unbind from RMI server
if (rmiPort > 0) {
Naming.unbind("//:" + rmiPort + "/" + appName);
}
// unbind from Jetty HTTP server // unbind from Jetty HTTP server
if (jetty != null) { if (jetty != null) {
if (appContext != null) { if (appContext != null) {

View file

@ -59,7 +59,7 @@ public class Server implements Runnable {
// server start time // server start time
public final long starttime; 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. // explicitly listed hosts.
public boolean paranoid; public boolean paranoid;
private ApplicationManager appManager; 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) { if (!config.hasXmlrpcPort() && sysProps.getProperty("xmlrpcPort") != null) {
try { try {
config.setXmlrpcPort(getInetSocketAddress(sysProps.getProperty("xmlrpcPort"))); config.setXmlrpcPort(getInetSocketAddress(sysProps.getProperty("xmlrpcPort")));
@ -238,12 +230,6 @@ public class Server implements Runnable {
config.setPropFile(new File(args[++i])); config.setPropFile(new File(args[++i]));
} else if (args[i].equals("-a") && ((i + 1) < args.length)) { } else if (args[i].equals("-a") && ((i + 1) < args.length)) {
config.setApps(StringUtils.split(args[++i])); 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)) { } else if (args[i].equals("-x") && ((i + 1) < args.length)) {
try { try {
config.setXmlrpcPort(getInetSocketAddress(args[++i])); 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(" -w [ip:]port Specify embedded web server address/port");
System.out.println(" -x [ip:]port Specify XML-RPC 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(" -jk [ip:]port Specify AJP13 address/port");
System.out.println(" -p [ip:]port Specify RMI address/port");
System.out.println(""); System.out.println("");
System.out.println("Supported formats for server ports:"); System.out.println("Supported formats for server ports:");
System.out.println(" <port-number>"); System.out.println(" <port-number>");
@ -359,10 +344,6 @@ public class Server implements Runnable {
checkPort(config.getWebsrvPort()); checkPort(config.getWebsrvPort());
} }
if (config.hasRmiPort()) {
checkPort(config.getRmiPort());
}
if (config.hasXmlrpcPort()) { if (config.hasXmlrpcPort()) {
checkPort(config.getXmlrpcPort()); checkPort(config.getXmlrpcPort());
} }
@ -588,34 +569,7 @@ public class Server implements Runnable {
logger.info("Starting XML-RPC server on port " + (xmlrpcPort)); logger.info("Starting XML-RPC server on port " + (xmlrpcPort));
} }
if (config.hasRmiPort()) { appManager = new ApplicationManager(appsProps, this);
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);
}
if (xmlrpc != null) { if (xmlrpc != null) {
xmlrpc.addHandler("$default", appManager); xmlrpc.addHandler("$default", appManager);

View file

@ -25,7 +25,6 @@ import java.net.InetSocketAddress;
public class ServerConfig { public class ServerConfig {
private InetSocketAddress rmiPort = null;
private InetSocketAddress xmlrpcPort = null; private InetSocketAddress xmlrpcPort = null;
private InetSocketAddress websrvPort = null; private InetSocketAddress websrvPort = null;
private InetSocketAddress ajp13Port = null; private InetSocketAddress ajp13Port = null;
@ -46,10 +45,6 @@ public class ServerConfig {
return (homeDir != null); return (homeDir != null);
} }
public boolean hasRmiPort() {
return (rmiPort != null);
}
public boolean hasXmlrpcPort() { public boolean hasXmlrpcPort() {
return (xmlrpcPort != null); return (xmlrpcPort != null);
} }
@ -66,14 +61,6 @@ public class ServerConfig {
return (apps != null); return (apps != null);
} }
public InetSocketAddress getRmiPort() {
return rmiPort;
}
public void setRmiPort(InetSocketAddress rmiPort) {
this.rmiPort = rmiPort;
}
public InetSocketAddress getXmlrpcPort() { public InetSocketAddress getXmlrpcPort() {
return xmlrpcPort; return xmlrpcPort;
} }

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -117,9 +117,6 @@ public final class DbMapping {
// timestamp of last modification of an object of this type // timestamp of last modification of an object of this type
long lastDataChange = 0; 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 // Set of mappings that depend on us and should be forwarded last data change events
HashSet dependentMappings = new HashSet(); HashSet dependentMappings = new HashSet();
@ -229,7 +226,6 @@ public final class DbMapping {
idField = props.getProperty("_id"); idField = props.getProperty("_id");
nameField = props.getProperty("_name"); nameField = props.getProperty("_name");
protoField = props.getProperty("_prototype"); protoField = props.getProperty("_prototype");
evictOnReplication = "true".equals(props.getProperty("_evictOnReplication"));
parentSetting = props.getProperty("_parent"); parentSetting = props.getProperty("_parent");
if (parentSetting != null) { if (parentSetting != null) {
@ -594,14 +590,6 @@ public final class DbMapping {
return protoField; 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. * Translate a database column name to an object property name according to this mapping.
*/ */

View file

@ -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;
}

View file

@ -34,8 +34,7 @@ import java.util.*;
* An implementation of INode that can be stored in the internal database or * An implementation of INode that can be stored in the internal database or
* an external relational database. * an external relational database.
*/ */
public final class Node implements INode, Serializable { public final class Node implements INode {
static final long serialVersionUID = -3740339688506633675L;
// The handle to the node's parent // The handle to the node's parent
protected NodeHandle parentHandle; protected NodeHandle parentHandle;
@ -60,7 +59,7 @@ public final class Node implements INode, Serializable {
private transient String prototype; private transient String prototype;
private transient NodeHandle handle; private transient NodeHandle handle;
private transient INode cacheNode; private transient INode cacheNode;
transient volatile WrappedNodeManager nmgr; transient final WrappedNodeManager nmgr;
transient DbMapping dbmap; transient DbMapping dbmap;
transient Key primaryKey = null; transient Key primaryKey = null;
transient String subnodeRelation = null; transient String subnodeRelation = null;
@ -70,14 +69,6 @@ public final class Node implements INode, Serializable {
transient private volatile int state; transient private volatile int state;
private static long idgen = 0; 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. * Creates an empty, uninitialized Node with the given create and modify time.
* This is used for null-node references in the node cache. * 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) { protected Node(long timestamp) {
created = lastmodified = 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. * Also used by embedded database to re-create an existing Node.
*/ */
public Node(String name, String id, String prototype, WrappedNodeManager nmgr) { public Node(String name, String id, String prototype, WrappedNodeManager nmgr) {
if (nmgr == null) {
throw new NullPointerException("nmgr");
}
this.nmgr = nmgr;
if (prototype == null) { if (prototype == null) {
prototype = "HopObject"; 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. * Initializer used for nodes being instanced from an embedded or relational database.
*/ */
public synchronized void init(DbMapping dbm, String id, String name, String prototype, public synchronized void init(DbMapping dbm, String id, String name,
Hashtable propMap, WrappedNodeManager nmgr) { String prototype, Hashtable propMap) {
if (nmgr == null) {
throw new NullPointerException("nmgr");
}
this.nmgr = nmgr;
this.dbmap = dbm; this.dbmap = dbm;
this.prototype = prototype; this.prototype = prototype;
this.id = id; 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 * used by Xml deserialization
*/ */
@ -1710,9 +1653,6 @@ public final class Node implements INode, Serializable {
if (n != null) { if (n != null) {
// do set DbMapping for embedded db collection nodes // do set DbMapping for embedded db collection nodes
n.setDbMapping(rel.getVirtualMapping()); n.setDbMapping(rel.getVirtualMapping());
// also set node manager in case this is a mountpoint node
// that came in through replication
n.nmgr = nmgr;
} }
} }
} }

View file

@ -42,7 +42,6 @@ public final class NodeManager {
protected IDGenerator idgen; protected IDGenerator idgen;
private boolean logSql; private boolean logSql;
private Log sqlLog = null; private Log sqlLog = null;
protected boolean logReplication;
private ArrayList listeners = new ArrayList(); private ArrayList listeners = new ArrayList();
// a wrapper that catches some Exceptions while accessing this NM // a wrapper that catches some Exceptions while accessing this NM
@ -77,19 +76,6 @@ public final class NodeManager {
} }
logSql = "true".equalsIgnoreCase(props.getProperty("logsql")); 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 = new XmlDatabase();
db.init(dbHome, app); db.init(dbHome, app);
@ -123,7 +109,6 @@ public final class NodeManager {
// notify the cache about the properties update // notify the cache about the properties update
cache.updateProperties(props); cache.updateProperties(props);
logSql = "true".equalsIgnoreCase(props.getProperty("logsql")); 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()) { if ((dbm == null) || !dbm.isRelational()) {
node = (Node) db.getNode(txn, kstr); node = (Node) db.getNode(txn, kstr);
node.nmgr = safe;
if ((node != null) && (dbm != null)) { if ((node != null) && (dbm != null)) {
node.setDbMapping(dbm); node.setDbMapping(dbm);
} }
@ -1388,15 +1371,11 @@ public final class NodeManager {
if (node == null && (dbm == null || !dbm.isRelational())) { if (node == null && (dbm == null || !dbm.isRelational())) {
node = (Node) db.getNode(txn, kstr); node = (Node) db.getNode(txn, kstr);
node.nmgr = safe;
} }
return node; return node;
} else if (rel == null || dbm == null || !dbm.isRelational()) { } else if (rel == null || dbm == null || !dbm.isRelational()) {
node = (Node) db.getNode(txn, kstr); node = (Node) db.getNode(txn, kstr);
node.nmgr = safe;
node.setDbMapping(dbm); node.setDbMapping(dbm);
return node; return node;
} else { } else {
Statement stmt = null; Statement stmt = null;
@ -1476,7 +1455,7 @@ public final class NodeManager {
String protoName = dbm.getTypeName(); String protoName = dbm.getTypeName();
DbMapping dbmap = dbm; DbMapping dbmap = dbm;
Node node = new Node(); Node node = new Node(safe);
for (int i = 0; i < columns.length; i++) { 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; 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) private void setStatementValue(PreparedStatement stmt, int columnNumber, String value, DbColumn col)
throws SQLException { throws SQLException {
if (value == null) { if (value == null) {

View file

@ -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;
}
}