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:
parent
e1354889ec
commit
fc1d8dfb26
12 changed files with 1324 additions and 495 deletions
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(" <port-number>");
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
950
src/helma/objectmodel/TransientNode.java
Normal file
950
src/helma/objectmodel/TransientNode.java
Normal 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;
|
||||
}
|
||||
}
|
346
src/helma/objectmodel/TransientProperty.java
Normal file
346
src/helma/objectmodel/TransientProperty.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue