Merged Stefan Pollach's XML Database branch.
This commit is contained in:
parent
ca2a42e204
commit
14a6f0840e
18 changed files with 629 additions and 470 deletions
|
@ -13,7 +13,6 @@ import helma.objectmodel.*;
|
||||||
import helma.objectmodel.db.*;
|
import helma.objectmodel.db.*;
|
||||||
import helma.xmlrpc.*;
|
import helma.xmlrpc.*;
|
||||||
import helma.util.*;
|
import helma.util.*;
|
||||||
import com.sleepycat.db.DbException;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
|
@ -231,7 +230,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
|
||||||
/**
|
/**
|
||||||
* Get the application ready to run, initializing the evaluators and type manager.
|
* Get the application ready to run, initializing the evaluators and type manager.
|
||||||
*/
|
*/
|
||||||
public void init () throws DbException, ScriptingException {
|
public void init () throws DatabaseException, ScriptingException {
|
||||||
scriptingEngine = new helma.scripting.fesi.FesiScriptingEnvironment ();
|
scriptingEngine = new helma.scripting.fesi.FesiScriptingEnvironment ();
|
||||||
scriptingEngine.init (this, props);
|
scriptingEngine.init (this, props);
|
||||||
|
|
||||||
|
@ -317,7 +316,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
|
||||||
// shut down node manager and embedded db
|
// shut down node manager and embedded db
|
||||||
try {
|
try {
|
||||||
nmgr.shutdown ();
|
nmgr.shutdown ();
|
||||||
} catch (DbException dbx) {
|
} catch (DatabaseException dbx) {
|
||||||
System.err.println ("Error shutting down embedded db: "+dbx);
|
System.err.println ("Error shutting down embedded db: "+dbx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ import helma.framework.*;
|
||||||
import helma.framework.core.*;
|
import helma.framework.core.*;
|
||||||
import helma.xmlrpc.*;
|
import helma.xmlrpc.*;
|
||||||
import helma.util.*;
|
import helma.util.*;
|
||||||
import com.sleepycat.db.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
49
src/helma/objectmodel/DatabaseException.java
Normal file
49
src/helma/objectmodel/DatabaseException.java
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// DatabaseException.java
|
||||||
|
|
||||||
|
package helma.objectmodel;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown on any kind of Database-Error
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class DatabaseException extends RuntimeException {
|
||||||
|
|
||||||
|
public DatabaseException (String msg) {
|
||||||
|
super (msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
35
src/helma/objectmodel/IDatabase.java
Normal file
35
src/helma/objectmodel/IDatabase.java
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// IDatabase.java
|
||||||
|
|
||||||
|
package helma.objectmodel;
|
||||||
|
|
||||||
|
import helma.objectmodel.db.IDGenerator;
|
||||||
|
import helma.objectmodel.INode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that is implemented by Database wrappers
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface IDatabase {
|
||||||
|
|
||||||
|
// db-related
|
||||||
|
public void shutdown ();
|
||||||
|
|
||||||
|
// id-related
|
||||||
|
public String nextID() throws ObjectNotFoundException;
|
||||||
|
public IDGenerator getIDGenerator (ITransaction transaction) throws Exception;
|
||||||
|
public void saveIDGenerator (ITransaction transaction, IDGenerator idgen) throws Exception;
|
||||||
|
|
||||||
|
// node-related
|
||||||
|
public INode getNode (ITransaction transaction, String key) throws Exception;
|
||||||
|
public void saveNode (ITransaction transaction, String key, INode node) throws Exception;
|
||||||
|
public void deleteNode (ITransaction transaction, String key) throws Exception;
|
||||||
|
|
||||||
|
// transaction-related
|
||||||
|
public ITransaction beginTransaction ();
|
||||||
|
public void commitTransaction (ITransaction transaction) throws DatabaseException;
|
||||||
|
public void abortTransaction (ITransaction transaction) throws DatabaseException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
18
src/helma/objectmodel/ITransaction.java
Normal file
18
src/helma/objectmodel/ITransaction.java
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// ITransaction.java
|
||||||
|
|
||||||
|
package helma.objectmodel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface is kept for databases that are able
|
||||||
|
* to run transactions. Transactions were used for the
|
||||||
|
* Berkeley database and might be used in other future
|
||||||
|
* databases, so we leave transactions in.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface ITransaction {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,292 +0,0 @@
|
||||||
// DbWrapper.java
|
|
||||||
// Copyright (c) Hannes Wallnöfer 1999-2000
|
|
||||||
|
|
||||||
|
|
||||||
package helma.objectmodel.db;
|
|
||||||
|
|
||||||
import com.sleepycat.db.*;
|
|
||||||
import helma.objectmodel.ObjectNotFoundException;
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper around a Berkeley embedded database. Used to gracefully handle the case
|
|
||||||
* when the native library can not be loaded.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class DbWrapper {
|
|
||||||
|
|
||||||
private boolean loaded, useTransactions;
|
|
||||||
|
|
||||||
private Db db;
|
|
||||||
DbEnv dbenv;
|
|
||||||
final int checkpointPause = 600000; // min. 10 minutes between checkpoints
|
|
||||||
volatile long lastCheckpoint = 0;
|
|
||||||
volatile long txncount=0;
|
|
||||||
|
|
||||||
private File dbBaseDir;
|
|
||||||
private NodeManager nmgr;
|
|
||||||
private String dbHome;
|
|
||||||
|
|
||||||
public DbWrapper (String dbHome, String dbFilename, NodeManager nmgr, boolean useTx) throws DbException {
|
|
||||||
|
|
||||||
this.dbHome = dbHome;
|
|
||||||
this.nmgr = nmgr;
|
|
||||||
|
|
||||||
try {
|
|
||||||
dbBaseDir = new File (dbHome);
|
|
||||||
if (!dbBaseDir.exists())
|
|
||||||
dbBaseDir.mkdirs();
|
|
||||||
|
|
||||||
useTransactions = useTx;
|
|
||||||
|
|
||||||
int dbInitFlags = Db.DB_CREATE | Db.DB_THREAD | Db.DB_INIT_MPOOL;
|
|
||||||
if (useTransactions) {
|
|
||||||
dbInitFlags = dbInitFlags | Db.DB_INIT_TXN;
|
|
||||||
}
|
|
||||||
|
|
||||||
dbenv = new DbEnv (0);
|
|
||||||
try {
|
|
||||||
dbenv.open (dbHome, dbInitFlags, 0); // for berkeley 3.0, add second parameter (null)
|
|
||||||
} catch (FileNotFoundException fnf) {
|
|
||||||
// we just created the dirs, so this shouldn't happen
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
dbenv.set_error_stream(System.err);
|
|
||||||
dbenv.set_errpfx("Sleepycat");
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Error in DbWrapper: "+e.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
db = new Db (dbenv, 0);
|
|
||||||
try {
|
|
||||||
db.upgrade (dbFilename, 0);
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
// nothing to upgrade, db doesn't exist
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
db.open (dbFilename, null, Db.DB_BTREE, Db.DB_CREATE, 0644);
|
|
||||||
} catch (FileNotFoundException fnf) {
|
|
||||||
// we just created the dirs, so this shouldn't happen
|
|
||||||
}
|
|
||||||
loaded = true;
|
|
||||||
|
|
||||||
} catch (NoClassDefFoundError noclass) {
|
|
||||||
nmgr.app.logEvent ("Warning: Using internal file based db as fallback.");
|
|
||||||
nmgr.app.logEvent ("Reason: "+noclass);
|
|
||||||
loaded = false;
|
|
||||||
} catch (UnsatisfiedLinkError nolib) {
|
|
||||||
nmgr.app.logEvent ("Warning: Using internal file based db as fallback.");
|
|
||||||
nmgr.app.logEvent ("Reason: "+nolib);
|
|
||||||
loaded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void shutdown () throws DbException {
|
|
||||||
if (loaded) {
|
|
||||||
db.close (0);
|
|
||||||
// closing the dbenv leads to segfault when app is restarted
|
|
||||||
// dbenv.close (0);
|
|
||||||
// dbenv.remove (dbHome, Db.DB_FORCE);
|
|
||||||
nmgr.app.logEvent ("Closed Berkeley DB");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DbTxn beginTransaction () throws DbException {
|
|
||||||
if (loaded && useTransactions)
|
|
||||||
return dbenv.txn_begin (null, 0);
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void commitTransaction (DbTxn txn) throws DbException {
|
|
||||||
if (txn == null || !loaded || !useTransactions)
|
|
||||||
return;
|
|
||||||
txn.commit (0);
|
|
||||||
if (++txncount%100 == 0 && System.currentTimeMillis()-checkpointPause > lastCheckpoint) {
|
|
||||||
// checkpoint transaction logs in time interval specified by server.checkpointPause
|
|
||||||
// if there are more then 100 transactions to checkpoint.
|
|
||||||
checkpoint ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void abortTransaction (DbTxn txn) throws DbException {
|
|
||||||
if (txn == null || !loaded || !useTransactions)
|
|
||||||
return;
|
|
||||||
txn.abort ();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void checkpoint () throws DbException {
|
|
||||||
if (!loaded || !useTransactions || txncount == 0)
|
|
||||||
return;
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
if (now - lastCheckpoint < checkpointPause)
|
|
||||||
return;
|
|
||||||
dbenv.txn_checkpoint (0, 0, 0); // for berkeley 3.0, remove third 0 parameter
|
|
||||||
txncount = 0;
|
|
||||||
lastCheckpoint = now;
|
|
||||||
nmgr.app.logEvent ("Spent "+(System.currentTimeMillis()-now)+" in checkpoint");
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDGenerator getIDGenerator (DbTxn txn, String kstr) throws Exception {
|
|
||||||
if (loaded)
|
|
||||||
return getIDGenFromDB (txn, kstr);
|
|
||||||
else
|
|
||||||
return getIDGenFromFile (kstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getNode (DbTxn txn, String kstr) throws Exception {
|
|
||||||
if (loaded)
|
|
||||||
return getNodeFromDB (txn, kstr);
|
|
||||||
else
|
|
||||||
return getNodeFromFile (kstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save (DbTxn txn, String kstr, Object obj) throws Exception {
|
|
||||||
if (loaded)
|
|
||||||
saveToDB (txn, kstr, obj);
|
|
||||||
else
|
|
||||||
saveToFile (kstr, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete (DbTxn txn, String kstr) throws Exception {
|
|
||||||
if (loaded)
|
|
||||||
deleteFromDB (txn, kstr);
|
|
||||||
else
|
|
||||||
deleteFromFile (kstr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private IDGenerator getIDGenFromDB (DbTxn txn, String kstr) throws Exception {
|
|
||||||
long now = System.currentTimeMillis ();
|
|
||||||
byte[] kbuf = kstr.getBytes ();
|
|
||||||
Dbt key = new Dbt (kbuf);
|
|
||||||
key.set_size (kbuf.length);
|
|
||||||
Dbt data = new Dbt ();
|
|
||||||
data.set_flags (Db.DB_DBT_MALLOC);
|
|
||||||
|
|
||||||
db.get (txn, key, data, 0);
|
|
||||||
|
|
||||||
byte[] b = data.get_data ();
|
|
||||||
if (b == null)
|
|
||||||
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
|
|
||||||
|
|
||||||
IDGenerator idgen = null;
|
|
||||||
ByteArrayInputStream bin = new ByteArrayInputStream (b);
|
|
||||||
ObjectInputStream oin = new ObjectInputStream (bin);
|
|
||||||
idgen = (IDGenerator) oin.readObject ();
|
|
||||||
|
|
||||||
oin.close ();
|
|
||||||
return idgen;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Node getNodeFromDB (DbTxn txn, String kstr) throws Exception {
|
|
||||||
long now = System.currentTimeMillis ();
|
|
||||||
byte[] kbuf = kstr.getBytes ();
|
|
||||||
Dbt key = new Dbt (kbuf);
|
|
||||||
key.set_size (kbuf.length);
|
|
||||||
Dbt data = new Dbt ();
|
|
||||||
data.set_flags (Db.DB_DBT_MALLOC);
|
|
||||||
|
|
||||||
db.get (txn, key, data, 0);
|
|
||||||
|
|
||||||
byte[] b = data.get_data ();
|
|
||||||
if (b == null)
|
|
||||||
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
|
|
||||||
|
|
||||||
Node node = null;
|
|
||||||
ByteArrayInputStream bin = new ByteArrayInputStream (b);
|
|
||||||
ObjectInputStream oin = new ObjectInputStream (bin);
|
|
||||||
node = (Node) oin.readObject ();
|
|
||||||
oin.close ();
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void saveToDB (DbTxn txn, String kstr, Object obj) throws Exception {
|
|
||||||
long now = System.currentTimeMillis ();
|
|
||||||
byte kbuf[] = kstr.getBytes();
|
|
||||||
ByteArrayOutputStream bout = new ByteArrayOutputStream ();
|
|
||||||
ObjectOutputStream oout = new ObjectOutputStream (bout);
|
|
||||||
oout.writeObject (obj);
|
|
||||||
oout.close ();
|
|
||||||
byte vbuf[] = bout.toByteArray ();
|
|
||||||
|
|
||||||
Dbt key = new Dbt (kbuf);
|
|
||||||
key.set_size (kbuf.length);
|
|
||||||
Dbt value = new Dbt (vbuf);
|
|
||||||
value.set_size (vbuf.length);
|
|
||||||
|
|
||||||
db.put (txn, key, value, 0);
|
|
||||||
// nmgr.app.logEvent ("saved "+obj+", size = "+vbuf.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteFromDB (DbTxn txn, String kstr) throws Exception {
|
|
||||||
|
|
||||||
byte kbuf[] = kstr.getBytes();
|
|
||||||
|
|
||||||
Dbt key = new Dbt (kbuf);
|
|
||||||
key.set_size (kbuf.length);
|
|
||||||
|
|
||||||
db.del (txn, key, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// File based fallback methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private IDGenerator getIDGenFromFile (String kstr) throws Exception {
|
|
||||||
|
|
||||||
File f = new File (dbBaseDir, kstr);
|
|
||||||
|
|
||||||
if ( ! f.exists() )
|
|
||||||
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
|
|
||||||
|
|
||||||
IDGenerator idgen = null;
|
|
||||||
FileInputStream bin = new FileInputStream (f);
|
|
||||||
ObjectInputStream oin = new ObjectInputStream (bin);
|
|
||||||
idgen = (IDGenerator) oin.readObject ();
|
|
||||||
|
|
||||||
oin.close ();
|
|
||||||
return idgen;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Node getNodeFromFile (String kstr) throws Exception {
|
|
||||||
|
|
||||||
File f = new File (dbBaseDir, kstr);
|
|
||||||
|
|
||||||
if ( ! f.exists() )
|
|
||||||
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
|
|
||||||
|
|
||||||
Node node = null;
|
|
||||||
FileInputStream bin = new FileInputStream (f);
|
|
||||||
ObjectInputStream oin = new ObjectInputStream (bin);
|
|
||||||
node = (Node) oin.readObject ();
|
|
||||||
oin.close ();
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void saveToFile (String kstr, Object obj) throws Exception {
|
|
||||||
|
|
||||||
File f = new File (dbBaseDir, kstr);
|
|
||||||
|
|
||||||
FileOutputStream bout = new FileOutputStream (f);
|
|
||||||
ObjectOutputStream oout = new ObjectOutputStream (bout);
|
|
||||||
oout.writeObject (obj);
|
|
||||||
oout.close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteFromFile (String kstr) throws Exception {
|
|
||||||
|
|
||||||
File f = new File (dbBaseDir, kstr);
|
|
||||||
f.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -58,7 +58,7 @@ public final class IDGenerator implements Serializable {
|
||||||
/**
|
/**
|
||||||
* Get the current counter value
|
* Get the current counter value
|
||||||
*/
|
*/
|
||||||
protected long getValue () {
|
public long getValue () {
|
||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,20 @@ public final class Node implements INode, Serializable {
|
||||||
out.writeObject (prototype);
|
out.writeObject (prototype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used by Xml deserialization
|
||||||
|
*/
|
||||||
|
public void setPropMap (Hashtable propMap) {
|
||||||
|
this.propMap = propMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used by Xml deserialization
|
||||||
|
*/
|
||||||
|
public void setSubnodes (List subnodes) {
|
||||||
|
this.subnodes = subnodes;
|
||||||
|
}
|
||||||
|
|
||||||
private transient String prototype;
|
private transient String prototype;
|
||||||
|
|
||||||
private transient NodeHandle handle;
|
private transient NodeHandle handle;
|
||||||
|
@ -164,8 +178,9 @@ public final class Node implements INode, Serializable {
|
||||||
/**
|
/**
|
||||||
* Creates a new Node with the given name. Only used by NodeManager for "root nodes" and
|
* Creates a new Node with the given name. Only used by NodeManager for "root nodes" and
|
||||||
* not in a Transaction context, which is why we can immediately mark it as CLEAN.
|
* not in a Transaction context, which is why we can immediately mark it as CLEAN.
|
||||||
|
* ADD: used by wrapped database to re-create an existing Node.
|
||||||
*/
|
*/
|
||||||
protected Node (String name, String id, String prototype, WrappedNodeManager nmgr) {
|
public Node (String name, String id, String prototype, WrappedNodeManager nmgr) {
|
||||||
this.nmgr = nmgr;
|
this.nmgr = nmgr;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name == null || "".equals (name) ? id : name;
|
this.name = name == null || "".equals (name) ? id : name;
|
||||||
|
@ -176,6 +191,17 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor used to create a Node with a given name from a wrapped database.
|
||||||
|
*/
|
||||||
|
public Node (String name, String id, String prototype, WrappedNodeManager nmgr, long created, long lastmodified) {
|
||||||
|
this (name,id,prototype,nmgr);
|
||||||
|
this.created = created;
|
||||||
|
this.lastmodified = lastmodified;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor used for virtual nodes.
|
* Constructor used for virtual nodes.
|
||||||
*/
|
*/
|
||||||
|
@ -459,6 +485,8 @@ public final class Node implements INode, Serializable {
|
||||||
} else {
|
} else {
|
||||||
anonymous = true;
|
anonymous = true;
|
||||||
}
|
}
|
||||||
|
} else if (p.contains (this) > -1) {
|
||||||
|
anonymous = true;
|
||||||
}
|
}
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
// just fall back to default method
|
// just fall back to default method
|
||||||
|
@ -577,6 +605,10 @@ public final class Node implements INode, Serializable {
|
||||||
parentHandle = parent == null ? null : parent.getHandle ();
|
parentHandle = parent == null ? null : parent.getHandle ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setParentHandle (NodeHandle parent) {
|
||||||
|
parentHandle = parent;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This version of setParent additionally marks the node as anonymous or non-anonymous,
|
* This version of setParent additionally marks the node as anonymous or non-anonymous,
|
||||||
* depending on the string argument. This is the version called from the scripting framework,
|
* depending on the string argument. This is the version called from the scripting framework,
|
||||||
|
@ -1197,6 +1229,10 @@ public final class Node implements INode, Serializable {
|
||||||
return new Enum ();
|
return new Enum ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List getSubnodeList() {
|
||||||
|
return subnodes;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean ignoreSubnodeChange () {
|
private boolean ignoreSubnodeChange () {
|
||||||
// return true if a change in subnodes can be ignored because it is
|
// return true if a change in subnodes can be ignored because it is
|
||||||
// stored in the subnodes themselves.
|
// stored in the subnodes themselves.
|
||||||
|
@ -1233,7 +1269,9 @@ public final class Node implements INode, Serializable {
|
||||||
// return propMap == null ? new Vector ().elements () : propMap.elements ();
|
// return propMap == null ? new Vector ().elements () : propMap.elements ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Hashtable getPropMap() {
|
||||||
|
return propMap;
|
||||||
|
}
|
||||||
|
|
||||||
public IProperty get (String propname, boolean inherit) {
|
public IProperty get (String propname, boolean inherit) {
|
||||||
return getProperty (propname, inherit);
|
return getProperty (propname, inherit);
|
||||||
|
|
|
@ -6,7 +6,6 @@ package helma.objectmodel.db;
|
||||||
import helma.util.CacheMap;
|
import helma.util.CacheMap;
|
||||||
import helma.objectmodel.*;
|
import helma.objectmodel.*;
|
||||||
import helma.framework.core.Application;
|
import helma.framework.core.Application;
|
||||||
import com.sleepycat.db.*;
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -26,7 +25,7 @@ public final class NodeManager {
|
||||||
|
|
||||||
private Replicator replicator;
|
private Replicator replicator;
|
||||||
|
|
||||||
protected DbWrapper db;
|
protected IDatabase db;
|
||||||
|
|
||||||
protected IDGenerator idgen;
|
protected IDGenerator idgen;
|
||||||
|
|
||||||
|
@ -42,7 +41,7 @@ public final class NodeManager {
|
||||||
* Create a new NodeManager for Application app. An embedded database will be
|
* Create a new NodeManager for Application app. An embedded database will be
|
||||||
* created in dbHome if one doesn't already exist.
|
* created in dbHome if one doesn't already exist.
|
||||||
*/
|
*/
|
||||||
public NodeManager (Application app, String dbHome, Properties props) throws DbException {
|
public NodeManager (Application app, String dbHome, Properties props) throws DatabaseException {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
int cacheSize = Integer.parseInt (props.getProperty ("cachesize", "1000"));
|
int cacheSize = Integer.parseInt (props.getProperty ("cachesize", "1000"));
|
||||||
// Make actual cache size bigger, since we use it only up to the threshold
|
// Make actual cache size bigger, since we use it only up to the threshold
|
||||||
|
@ -68,7 +67,7 @@ public final class NodeManager {
|
||||||
idBaseValue = Math.max (1l, idBaseValue); // 0 and 1 are reserved for root nodes
|
idBaseValue = Math.max (1l, idBaseValue); // 0 and 1 are reserved for root nodes
|
||||||
} catch (NumberFormatException ignore) {}
|
} catch (NumberFormatException ignore) {}
|
||||||
|
|
||||||
db = new DbWrapper (dbHome, helma.main.Server.dbFilename, this, helma.main.Server.useTransactions);
|
db = new XmlDatabase (dbHome, helma.main.Server.dbFilename, this);
|
||||||
initDb ();
|
initDb ();
|
||||||
|
|
||||||
logSql = "true".equalsIgnoreCase(props.getProperty ("logsql"));
|
logSql = "true".equalsIgnoreCase(props.getProperty ("logsql"));
|
||||||
|
@ -77,44 +76,44 @@ public final class NodeManager {
|
||||||
/**
|
/**
|
||||||
* Method used to create the root node and id-generator, if they don't exist already.
|
* Method used to create the root node and id-generator, if they don't exist already.
|
||||||
*/
|
*/
|
||||||
public void initDb () throws DbException {
|
public void initDb () throws DatabaseException {
|
||||||
|
|
||||||
DbTxn txn = null;
|
ITransaction txn = null;
|
||||||
try {
|
try {
|
||||||
txn = db.beginTransaction ();
|
txn = db.beginTransaction ();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
idgen = db.getIDGenerator (txn, "idgen");
|
idgen = db.getIDGenerator (txn);
|
||||||
if (idgen.getValue() < idBaseValue) {
|
if (idgen.getValue() < idBaseValue) {
|
||||||
idgen.setValue (idBaseValue);
|
idgen.setValue (idBaseValue);
|
||||||
db.save (txn, "idgen", idgen);
|
db.saveIDGenerator (txn, idgen);
|
||||||
}
|
}
|
||||||
} catch (ObjectNotFoundException notfound) {
|
} catch (ObjectNotFoundException notfound) {
|
||||||
// will start with idBaseValue+1
|
// will start with idBaseValue+1
|
||||||
idgen = new IDGenerator (idBaseValue);
|
idgen = new IDGenerator (idBaseValue);
|
||||||
db.save (txn, "idgen", idgen);
|
db.saveIDGenerator (txn, idgen);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we need to set the id generator to a base value
|
// check if we need to set the id generator to a base value
|
||||||
|
|
||||||
Node node = null;
|
Node node = null;
|
||||||
try {
|
try {
|
||||||
node = db.getNode (txn, "0");
|
node = (Node)db.getNode (txn, "0");
|
||||||
node.nmgr = safe;
|
node.nmgr = safe;
|
||||||
} catch (ObjectNotFoundException notfound) {
|
} catch (ObjectNotFoundException notfound) {
|
||||||
node = new Node ("root", "0", "root", safe);
|
node = new Node ("root", "0", "root", safe);
|
||||||
node.setDbMapping (app.getDbMapping ("root"));
|
node.setDbMapping (app.getDbMapping ("root"));
|
||||||
db.save (txn, node.getID (), node);
|
db.saveNode (txn, node.getID (), node);
|
||||||
registerNode (node); // register node with nodemanager cache
|
registerNode (node); // register node with nodemanager cache
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
node = db.getNode (txn, "1");
|
node = (Node)db.getNode (txn, "1");
|
||||||
node.nmgr = safe;
|
node.nmgr = safe;
|
||||||
} catch (ObjectNotFoundException notfound) {
|
} catch (ObjectNotFoundException notfound) {
|
||||||
node = new Node ("users", "1", null, safe);
|
node = new Node ("users", "1", null, safe);
|
||||||
node.setDbMapping (app.getDbMapping ("__userroot__"));
|
node.setDbMapping (app.getDbMapping ("__userroot__"));
|
||||||
db.save (txn, node.getID (), node);
|
db.saveNode (txn, node.getID (), node);
|
||||||
registerNode (node); // register node with nodemanager cache
|
registerNode (node); // register node with nodemanager cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +124,7 @@ public final class NodeManager {
|
||||||
try {
|
try {
|
||||||
db.abortTransaction (txn);
|
db.abortTransaction (txn);
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
throw (new DbException ("Error initializing db"));
|
throw (new DatabaseException ("Error initializing db"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +133,7 @@ public final class NodeManager {
|
||||||
* Shut down this node manager. This is called when the application using this
|
* Shut down this node manager. This is called when the application using this
|
||||||
* node manager is stopped.
|
* node manager is stopped.
|
||||||
*/
|
*/
|
||||||
public void shutdown () throws DbException {
|
public void shutdown () throws DatabaseException {
|
||||||
db.shutdown ();
|
db.shutdown ();
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
synchronized (cache) {
|
synchronized (cache) {
|
||||||
|
@ -360,7 +359,7 @@ public final class NodeManager {
|
||||||
* Insert a new node in the embedded database or a relational database table, depending
|
* Insert a new node in the embedded database or a relational database table, depending
|
||||||
* on its db mapping.
|
* on its db mapping.
|
||||||
*/
|
*/
|
||||||
public void insertNode (DbWrapper db, DbTxn txn, Node node) throws Exception {
|
public void insertNode (IDatabase db, ITransaction txn, Node node) throws Exception {
|
||||||
|
|
||||||
Transactor tx = (Transactor) Thread.currentThread ();
|
Transactor tx = (Transactor) Thread.currentThread ();
|
||||||
// tx.timer.beginEvent ("insertNode "+node);
|
// tx.timer.beginEvent ("insertNode "+node);
|
||||||
|
@ -368,7 +367,7 @@ public final class NodeManager {
|
||||||
DbMapping dbm = node.getDbMapping ();
|
DbMapping dbm = node.getDbMapping ();
|
||||||
|
|
||||||
if (dbm == null || !dbm.isRelational ()) {
|
if (dbm == null || !dbm.isRelational ()) {
|
||||||
db.save (txn, node.getID (), node);
|
db.saveNode (txn, node.getID (), node);
|
||||||
} else {
|
} else {
|
||||||
app.logEvent ("inserting relational node: "+node.getID ());
|
app.logEvent ("inserting relational node: "+node.getID ());
|
||||||
TableDataSet tds = null;
|
TableDataSet tds = null;
|
||||||
|
@ -438,7 +437,7 @@ public final class NodeManager {
|
||||||
* Updates a modified node in the embedded db or an external relational database, depending
|
* Updates a modified node in the embedded db or an external relational database, depending
|
||||||
* on its database mapping.
|
* on its database mapping.
|
||||||
*/
|
*/
|
||||||
public void updateNode (DbWrapper db, DbTxn txn, Node node) throws Exception {
|
public void updateNode (IDatabase db, ITransaction txn, Node node) throws Exception {
|
||||||
|
|
||||||
Transactor tx = (Transactor) Thread.currentThread ();
|
Transactor tx = (Transactor) Thread.currentThread ();
|
||||||
// tx.timer.beginEvent ("updateNode "+node);
|
// tx.timer.beginEvent ("updateNode "+node);
|
||||||
|
@ -446,7 +445,7 @@ public final class NodeManager {
|
||||||
DbMapping dbm = node.getDbMapping ();
|
DbMapping dbm = node.getDbMapping ();
|
||||||
|
|
||||||
if (dbm == null || !dbm.isRelational ()) {
|
if (dbm == null || !dbm.isRelational ()) {
|
||||||
db.save (txn, node.getID (), node);
|
db.saveNode (txn, node.getID (), node);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
TableDataSet tds = null;
|
TableDataSet tds = null;
|
||||||
|
@ -537,7 +536,7 @@ public final class NodeManager {
|
||||||
/**
|
/**
|
||||||
* Performs the actual deletion of a node from either the embedded or an external SQL database.
|
* Performs the actual deletion of a node from either the embedded or an external SQL database.
|
||||||
*/
|
*/
|
||||||
public void deleteNode (DbWrapper db, DbTxn txn, Node node) throws Exception {
|
public void deleteNode (IDatabase db, ITransaction txn, Node node) throws Exception {
|
||||||
|
|
||||||
Transactor tx = (Transactor) Thread.currentThread ();
|
Transactor tx = (Transactor) Thread.currentThread ();
|
||||||
// tx.timer.beginEvent ("deleteNode "+node);
|
// tx.timer.beginEvent ("deleteNode "+node);
|
||||||
|
@ -545,7 +544,7 @@ public final class NodeManager {
|
||||||
DbMapping dbm = node.getDbMapping ();
|
DbMapping dbm = node.getDbMapping ();
|
||||||
|
|
||||||
if (dbm == null || !dbm.isRelational ()) {
|
if (dbm == null || !dbm.isRelational ()) {
|
||||||
db.delete (txn, node.getID ());
|
db.deleteNode (txn, node.getID ());
|
||||||
} else {
|
} else {
|
||||||
Statement st = null;
|
Statement st = null;
|
||||||
try {
|
try {
|
||||||
|
@ -865,14 +864,14 @@ public final class NodeManager {
|
||||||
// private getNode methods
|
// private getNode methods
|
||||||
///////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private Node getNodeByKey (DbTxn txn, DbKey key) throws Exception {
|
private Node getNodeByKey (ITransaction txn, DbKey key) throws Exception {
|
||||||
// Note: Key must be a DbKey, otherwise will not work for relational objects
|
// Note: Key must be a DbKey, otherwise will not work for relational objects
|
||||||
Node node = null;
|
Node node = null;
|
||||||
DbMapping dbm = app.getDbMapping (key.getStorageName ());
|
DbMapping dbm = app.getDbMapping (key.getStorageName ());
|
||||||
String kstr = key.getID ();
|
String kstr = key.getID ();
|
||||||
|
|
||||||
if (dbm == null || !dbm.isRelational ()) {
|
if (dbm == null || !dbm.isRelational ()) {
|
||||||
node = db.getNode (txn, kstr);
|
node = (Node)db.getNode (txn, kstr);
|
||||||
node.nmgr = safe;
|
node.nmgr = safe;
|
||||||
if (node != null && dbm != null)
|
if (node != null && dbm != null)
|
||||||
node.setDbMapping (dbm);
|
node.setDbMapping (dbm);
|
||||||
|
@ -905,7 +904,7 @@ public final class NodeManager {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node getNodeByRelation (DbTxn txn, Node home, String kstr, Relation rel) throws Exception {
|
private Node getNodeByRelation (ITransaction txn, Node home, String kstr, Relation rel) throws Exception {
|
||||||
Node node = null;
|
Node node = null;
|
||||||
|
|
||||||
if (rel.virtual) {
|
if (rel.virtual) {
|
||||||
|
@ -922,13 +921,13 @@ public final class NodeManager {
|
||||||
} else if (rel != null && rel.groupby != null) {
|
} else if (rel != null && rel.groupby != null) {
|
||||||
node = home.getGroupbySubnode (kstr, false);
|
node = home.getGroupbySubnode (kstr, false);
|
||||||
if (node == null && (rel.otherType == null || !rel.otherType.isRelational ())) {
|
if (node == null && (rel.otherType == null || !rel.otherType.isRelational ())) {
|
||||||
node = db.getNode (txn, kstr);
|
node = (Node)db.getNode (txn, kstr);
|
||||||
node.nmgr = safe;
|
node.nmgr = safe;
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
|
|
||||||
} else if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) {
|
} else if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) {
|
||||||
node = db.getNode (txn, kstr);
|
node = (Node)db.getNode (txn, kstr);
|
||||||
node.nmgr = safe;
|
node.nmgr = safe;
|
||||||
node.setDbMapping (rel.otherType);
|
node.setDbMapping (rel.otherType);
|
||||||
return node;
|
return node;
|
||||||
|
|
|
@ -210,6 +210,17 @@ public final class Property implements IProperty, Serializable, Cloneable {
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNodeHandle (NodeHandle value) {
|
||||||
|
if (type == NODE)
|
||||||
|
unregisterNode ();
|
||||||
|
if (type == JAVAOBJECT)
|
||||||
|
this.jvalue = null;
|
||||||
|
// registerNode (value);
|
||||||
|
type = NODE;
|
||||||
|
nhandle = value;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void setJavaObjectValue (Object value) {
|
public void setJavaObjectValue (Object value) {
|
||||||
if (type == NODE)
|
if (type == NODE)
|
||||||
unregisterNode ();
|
unregisterNode ();
|
||||||
|
|
|
@ -9,7 +9,6 @@ import java.sql.*;
|
||||||
import helma.objectmodel.*;
|
import helma.objectmodel.*;
|
||||||
import helma.util.Timer;
|
import helma.util.Timer;
|
||||||
import helma.framework.TimeoutException;
|
import helma.framework.TimeoutException;
|
||||||
import com.sleepycat.db.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A subclass of thread that keeps track of changed nodes and triggers
|
* A subclass of thread that keeps track of changed nodes and triggers
|
||||||
|
@ -30,7 +29,7 @@ public class Transactor extends Thread {
|
||||||
private volatile boolean killed;
|
private volatile boolean killed;
|
||||||
|
|
||||||
// Transaction for the embedded database
|
// Transaction for the embedded database
|
||||||
protected DbTxn txn;
|
protected ITransaction txn;
|
||||||
// Transactions for SQL data sources
|
// Transactions for SQL data sources
|
||||||
protected HashMap sqlCon;
|
protected HashMap sqlCon;
|
||||||
|
|
||||||
|
@ -108,7 +107,7 @@ public class Transactor extends Thread {
|
||||||
public synchronized void begin (String tnm) throws Exception {
|
public synchronized void begin (String tnm) throws Exception {
|
||||||
|
|
||||||
if (killed)
|
if (killed)
|
||||||
throw new DbException ("Transaction started on killed thread");
|
throw new DatabaseException ("Transaction started on killed thread");
|
||||||
|
|
||||||
if (active)
|
if (active)
|
||||||
abort ();
|
abort ();
|
||||||
|
@ -171,7 +170,7 @@ public class Transactor extends Thread {
|
||||||
cleannodes.clear ();
|
cleannodes.clear ();
|
||||||
|
|
||||||
if (nmgr.idgen.dirty) {
|
if (nmgr.idgen.dirty) {
|
||||||
nmgr.db.save (txn, "idgen", nmgr.idgen);
|
nmgr.db.saveIDGenerator (txn, nmgr.idgen);
|
||||||
nmgr.idgen.dirty = false;
|
nmgr.idgen.dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
95
src/helma/objectmodel/db/XmlDatabase.java
Normal file
95
src/helma/objectmodel/db/XmlDatabase.java
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
package helma.objectmodel.db;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Date;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
|
import helma.objectmodel.*;
|
||||||
|
import helma.objectmodel.dom.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple XML-database
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class XmlDatabase implements IDatabase {
|
||||||
|
|
||||||
|
private String dbHome;
|
||||||
|
private File dbBaseDir;
|
||||||
|
private NodeManager nmgr;
|
||||||
|
private IDGenerator idgen;
|
||||||
|
// character encoding to use when writing files.
|
||||||
|
// use standard encoding by default.
|
||||||
|
private String encoding = null;
|
||||||
|
|
||||||
|
public XmlDatabase (String dbHome, String dbFilename, NodeManager nmgr) throws DatabaseException {
|
||||||
|
this.dbHome = dbHome;
|
||||||
|
this.nmgr = nmgr;
|
||||||
|
dbBaseDir = new File (dbHome);
|
||||||
|
if (!dbBaseDir.exists() && !dbBaseDir.mkdirs() )
|
||||||
|
throw new RuntimeException("Couldn't create DB-directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown () { }
|
||||||
|
public ITransaction beginTransaction () throws DatabaseException { return null; }
|
||||||
|
public void commitTransaction (ITransaction txn) throws DatabaseException { }
|
||||||
|
public void abortTransaction (ITransaction txn) throws DatabaseException { }
|
||||||
|
|
||||||
|
public String nextID() throws ObjectNotFoundException {
|
||||||
|
if (idgen==null) {
|
||||||
|
getIDGenerator(null);
|
||||||
|
}
|
||||||
|
return idgen.newID();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDGenerator getIDGenerator (ITransaction txn) throws ObjectNotFoundException {
|
||||||
|
File file = new File (dbBaseDir, "idgen.xml");
|
||||||
|
this.idgen = IDGenParser.getIDGenerator(file);
|
||||||
|
return idgen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveIDGenerator (ITransaction txn, IDGenerator idgen) throws Exception {
|
||||||
|
File file = new File (dbBaseDir, "idgen.xml");
|
||||||
|
IDGenParser.saveIDGenerator(idgen,file);
|
||||||
|
this.idgen = idgen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INode getNode (ITransaction txn, String kstr) throws Exception {
|
||||||
|
File f = new File (dbBaseDir, kstr+".xml");
|
||||||
|
if ( ! f.exists() )
|
||||||
|
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
|
||||||
|
try {
|
||||||
|
XmlReader reader = new XmlReader (nmgr);
|
||||||
|
Node node = (Node)reader.read (f, null);
|
||||||
|
return node;
|
||||||
|
} catch ( RuntimeException x ) {
|
||||||
|
nmgr.app.logEvent("error reading node from XmlDatbase: " + x.toString() );
|
||||||
|
throw new ObjectNotFoundException(x.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveNode (ITransaction txn, String kstr, INode node) throws Exception {
|
||||||
|
XmlWriter writer = null;
|
||||||
|
File file = new File (dbBaseDir,kstr+".xml");
|
||||||
|
if (encoding != null)
|
||||||
|
writer = new XmlWriter (file, encoding);
|
||||||
|
else
|
||||||
|
writer = new XmlWriter (file);
|
||||||
|
writer.setMaxLevels(1);
|
||||||
|
boolean result = writer.write((Node)node);
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteNode (ITransaction txn, String kstr) throws Exception {
|
||||||
|
File f = new File (dbBaseDir, kstr+".xml");
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncoding (String enc) {
|
||||||
|
this.encoding = encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncoding () {
|
||||||
|
return encoding;
|
||||||
|
}
|
||||||
|
}
|
37
src/helma/objectmodel/dom/IDGenParser.java
Normal file
37
src/helma/objectmodel/dom/IDGenParser.java
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package helma.objectmodel.dom;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Date;
|
||||||
|
import org.w3c.dom.*;
|
||||||
|
|
||||||
|
import helma.objectmodel.ObjectNotFoundException;
|
||||||
|
import helma.objectmodel.db.IDGenerator;
|
||||||
|
|
||||||
|
public class IDGenParser {
|
||||||
|
|
||||||
|
public static IDGenerator getIDGenerator (File file) throws ObjectNotFoundException {
|
||||||
|
if ( ! file.exists() )
|
||||||
|
throw new ObjectNotFoundException ("IDGenerator not found in idgen.xml");
|
||||||
|
try {
|
||||||
|
Document document = XmlUtil.parse(new FileInputStream (file));
|
||||||
|
org.w3c.dom.Element tmp = (Element)document.getDocumentElement().getElementsByTagName("counter").item(0);
|
||||||
|
return new IDGenerator( Long.parseLong (XmlUtil.getTextContent(tmp)) );
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ObjectNotFoundException(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IDGenerator saveIDGenerator (IDGenerator idgen, File file) throws Exception {
|
||||||
|
OutputStreamWriter out = new OutputStreamWriter (new FileOutputStream (file));
|
||||||
|
out.write ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||||
|
out.write ("<!-- printed by helma object publisher -->\n");
|
||||||
|
out.write ("<!-- created " + (new Date()).toString() + " -->\n" );
|
||||||
|
out.write ("<xmlroot>\n");
|
||||||
|
out.write (" <counter>" + idgen.getValue() + "</counter>\n");
|
||||||
|
out.write ("</xmlroot>\n");
|
||||||
|
out.close ();
|
||||||
|
return idgen;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package helma.objectmodel.dom;
|
||||||
|
|
||||||
public interface XmlConstants {
|
public interface XmlConstants {
|
||||||
|
|
||||||
public final String NAMESPACE = "http://www.helma.org/";
|
public final String NAMESPACE = "http://www.helma.org/docs/guide/features/database";
|
||||||
public final String DATEFORMAT = "dd.MM.yyyy HH:mm:ss z";
|
public final String DATEFORMAT = "dd.MM.yyyy HH:mm:ss z";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,8 @@ public class XmlConverter implements XmlConstants {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: handle CDATA!
|
||||||
|
|
||||||
// it's some kind of element (property or child)
|
// it's some kind of element (property or child)
|
||||||
if ( childNode.getNodeType()==org.w3c.dom.Node.ELEMENT_NODE ) {
|
if ( childNode.getNodeType()==org.w3c.dom.Node.ELEMENT_NODE ) {
|
||||||
|
|
||||||
|
|
|
@ -1,91 +1,95 @@
|
||||||
package helma.objectmodel.dom;
|
package helma.objectmodel.dom;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
import java.net.*;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.*;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.xml.parsers.*;
|
|
||||||
import org.w3c.dom.*;
|
import org.w3c.dom.*;
|
||||||
|
|
||||||
import helma.objectmodel.*;
|
import helma.objectmodel.INode;
|
||||||
|
import helma.objectmodel.db.DbKey;
|
||||||
|
import helma.objectmodel.db.ExternalizableVector;
|
||||||
|
import helma.objectmodel.db.Node;
|
||||||
|
import helma.objectmodel.db.NodeHandle;
|
||||||
|
import helma.objectmodel.db.NodeManager;
|
||||||
|
import helma.objectmodel.db.Property;
|
||||||
|
|
||||||
public class XmlReader implements XmlConstants {
|
public class XmlReader implements XmlConstants {
|
||||||
|
|
||||||
private int offset = 0;
|
|
||||||
private HashMap convertedNodes;
|
private HashMap convertedNodes;
|
||||||
|
private NodeManager nmgr = null;
|
||||||
|
|
||||||
public XmlReader() {
|
public XmlReader () {
|
||||||
}
|
}
|
||||||
|
|
||||||
public INode read( String desc ) {
|
public XmlReader (NodeManager nmgr) {
|
||||||
return read(desc, new TransientNode() );
|
this.nmgr = nmgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public INode read( String desc, INode helmaNode ) throws RuntimeException {
|
/**
|
||||||
|
* main entry to read an xml-file.
|
||||||
|
*/
|
||||||
|
public INode read (File file, INode helmaNode) throws RuntimeException {
|
||||||
try {
|
try {
|
||||||
return read( new File(desc), helmaNode );
|
return read (new FileInputStream(file), helmaNode);
|
||||||
} catch ( FileNotFoundException notfound ) {
|
} catch (FileNotFoundException notfound) {
|
||||||
throw new RuntimeException( "couldn't find xml-file: " + desc );
|
System.err.println ("couldn't find xml-file: " + file.getAbsolutePath ());
|
||||||
} catch ( IOException ioerror ) {
|
|
||||||
throw new RuntimeException( "couldn't read xml: " + desc );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public INode read( File file, INode helmaNode ) throws RuntimeException, FileNotFoundException {
|
|
||||||
return read( new FileInputStream(file), helmaNode );
|
|
||||||
}
|
|
||||||
|
|
||||||
public INode read( InputStream in, INode helmaNode ) throws RuntimeException {
|
|
||||||
Document document = XmlUtil.parse(in);
|
|
||||||
if ( document!=null && document.getDocumentElement()!=null ) {
|
|
||||||
Node tmp = document.getDocumentElement().getFirstChild();
|
|
||||||
Element workelement = null;
|
|
||||||
while( tmp!=null ) {
|
|
||||||
tmp = tmp.getNextSibling();
|
|
||||||
if ( tmp.getNodeType()==Node.ELEMENT_NODE ) {
|
|
||||||
workelement = (Element) tmp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return startConversion( helmaNode, workelement );
|
|
||||||
} else {
|
|
||||||
return helmaNode;
|
return helmaNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public INode startConversion( INode helmaNode, Element element ) {
|
/**
|
||||||
convertedNodes = new HashMap();
|
* read an InputStream with xml-content.
|
||||||
INode convertedNode = convert(helmaNode, element );
|
*/
|
||||||
|
public INode read (InputStream in, INode helmaNode) throws RuntimeException {
|
||||||
|
if (helmaNode==null && nmgr==null)
|
||||||
|
throw new RuntimeException ("can't create a new Node without a NodeManager");
|
||||||
|
Document document = XmlUtil.parse (in);
|
||||||
|
Element element = XmlUtil.getFirstElement(document);
|
||||||
|
if (element==null)
|
||||||
|
throw new RuntimeException ("corrupted xml-file");
|
||||||
|
|
||||||
|
if (helmaNode==null) {
|
||||||
|
return convert (element);
|
||||||
|
} else {
|
||||||
|
convertedNodes = new HashMap ();
|
||||||
|
INode convertedNode = convert (element, helmaNode);
|
||||||
convertedNodes = null;
|
convertedNodes = null;
|
||||||
return convertedNode;
|
return convertedNode;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public INode convert( INode helmaNode, Element element ) {
|
/**
|
||||||
offset++;
|
* convert children of an Element to a given helmaNode
|
||||||
String idref = element.getAttributeNS(NAMESPACE, "idref");
|
*/
|
||||||
String key = idref + "-" + element.getAttributeNS(NAMESPACE, "prototyperef");
|
public INode convert (Element element, INode helmaNode) {
|
||||||
|
String idref = element.getAttribute("idref");
|
||||||
|
String key = idref + "-" + element.getAttribute("prototyperef");
|
||||||
if( idref!=null && !idref.equals("") ) {
|
if( idref!=null && !idref.equals("") ) {
|
||||||
if( convertedNodes.containsKey(key) ) {
|
if( convertedNodes.containsKey(key) ) {
|
||||||
offset--;
|
|
||||||
return (INode)convertedNodes.get(key);
|
return (INode)convertedNodes.get(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key = element.getAttributeNS(NAMESPACE, "id") + "-" + element.getAttributeNS(NAMESPACE, "prototype");
|
key = element.getAttribute("id") + "-" + element.getAttribute("prototype");
|
||||||
convertedNodes.put( key, helmaNode );
|
convertedNodes.put( key, helmaNode );
|
||||||
|
String prototype = element.getAttribute("prototype");
|
||||||
// FIXME: import id on persistent nodes
|
|
||||||
String prototype = element.getAttributeNS(NAMESPACE, "prototype");
|
|
||||||
if( !prototype.equals("") && !prototype.equals("hopobject") ) {
|
if( !prototype.equals("") && !prototype.equals("hopobject") ) {
|
||||||
helmaNode.setPrototype( prototype );
|
helmaNode.setPrototype( prototype );
|
||||||
}
|
}
|
||||||
children(helmaNode, element);
|
children(helmaNode, element);
|
||||||
offset--;
|
|
||||||
return helmaNode;
|
return helmaNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// used by convert(Element,INode)
|
||||||
private INode children( INode helmaNode, Element element ) {
|
private INode children( INode helmaNode, Element element ) {
|
||||||
NodeList list = element.getChildNodes();
|
NodeList list = element.getChildNodes();
|
||||||
int len = list.getLength();
|
int len = list.getLength();
|
||||||
|
@ -94,16 +98,23 @@ public class XmlReader implements XmlConstants {
|
||||||
try {
|
try {
|
||||||
childElement = (Element)list.item(i);
|
childElement = (Element)list.item(i);
|
||||||
} catch( ClassCastException e ) {
|
} catch( ClassCastException e ) {
|
||||||
continue;
|
continue; // ignore CDATA, comments etc
|
||||||
}
|
}
|
||||||
INode workNode = null;
|
INode workNode = null;
|
||||||
|
|
||||||
if ( childElement.getTagName().equals("hop:child") ) {
|
if ( childElement.getTagName().equals("hop:child") ) {
|
||||||
convert( helmaNode.createNode(null), childElement );
|
|
||||||
} else if ( !"".equals(childElement.getAttributeNS(NAMESPACE,"id")) || !"".equals(childElement.getAttributeNS(NAMESPACE,"idref")) ) {
|
convert (childElement, helmaNode.createNode(null));
|
||||||
// we've got an object!
|
|
||||||
helmaNode.setNode( childElement.getTagName(), convert( helmaNode.createNode(childElement.getTagName()), childElement ) );
|
} else if ( !"".equals(childElement.getAttribute("id")) || !"".equals(childElement.getAttribute("idref")) ) {
|
||||||
|
|
||||||
|
String childTagName = childElement.getTagName();
|
||||||
|
INode newNode = convert (childElement, helmaNode.createNode (childTagName));
|
||||||
|
helmaNode.setNode (childTagName, newNode);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
String type = childElement.getAttribute("hop:type");
|
|
||||||
|
String type = childElement.getAttribute("type");
|
||||||
String key = childElement.getTagName();
|
String key = childElement.getTagName();
|
||||||
String content = XmlUtil.getTextContent(childElement);
|
String content = XmlUtil.getTextContent(childElement);
|
||||||
if ( type.equals("boolean") ) {
|
if ( type.equals("boolean") ) {
|
||||||
|
@ -132,26 +143,101 @@ public class XmlReader implements XmlConstants {
|
||||||
return helmaNode;
|
return helmaNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** for testing */
|
|
||||||
public static void main ( String args[] ) throws Exception {
|
/**
|
||||||
|
* This is a basic de-serialization method for XML-2-Node conversion.
|
||||||
|
* It reads a Node from a database-like file and should return a Node
|
||||||
|
* that matches exactly the one dumped to that file before.
|
||||||
|
* It only supports persistent-capable Nodes (from objectmodel.db-package).
|
||||||
|
*/
|
||||||
|
public helma.objectmodel.db.Node convert (Element element) {
|
||||||
|
// FIXME: this method should use Element.getAttributeNS():
|
||||||
|
// FIXME: do we need the name value or is it retrieved through mappings anyway?
|
||||||
|
String name = element.getAttribute("name");
|
||||||
|
// String name = null;
|
||||||
|
String id = element.getAttribute("id");
|
||||||
|
String prototype = element.getAttribute("prototype");
|
||||||
|
if ( "".equals(prototype) )
|
||||||
|
prototype = "hopobject";
|
||||||
|
helma.objectmodel.db.Node helmaNode = null;
|
||||||
try {
|
try {
|
||||||
XmlReader x = new XmlReader ();
|
long created = Long.parseLong (element.getAttribute ("created"));
|
||||||
INode node = x.read("test.xml");
|
long lastmodified = Long.parseLong (element.getAttribute ("lastModified"));
|
||||||
} catch ( Exception e ) {
|
helmaNode = new helma.objectmodel.db.Node (name,id,prototype,nmgr.safe,created,lastmodified);
|
||||||
System.out.println("exception " + e.toString() );
|
} catch ( NumberFormatException e ) {
|
||||||
throw new RuntimeException(e.toString());
|
helmaNode = new helma.objectmodel.db.Node (name,id,prototype,nmgr.safe);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now loop through all child elements and retrieve properties/subnodes for this node.
|
||||||
|
NodeList list = element.getChildNodes();
|
||||||
|
int len = list.getLength();
|
||||||
|
Hashtable propMap = new Hashtable();
|
||||||
|
List subnodes = new ExternalizableVector();
|
||||||
|
for ( int i=0; i<len; i++ ) {
|
||||||
|
|
||||||
/** for testing */
|
Element childElement;
|
||||||
void debug(Object msg) {
|
try {
|
||||||
for ( int i=0; i<offset; i++ ) {
|
childElement = (Element)list.item(i);
|
||||||
System.out.print(" ");
|
} catch( ClassCastException e ) {
|
||||||
}
|
continue; // ignore CDATA, comments etc
|
||||||
System.out.println(msg.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( childElement.getTagName().equals("hop:child") ) {
|
||||||
|
// add a new NodeHandle, presume all IDs in this objectcache are unique,
|
||||||
|
// a prerequisite for a simple internal database.
|
||||||
|
subnodes.add (new NodeHandle (new DbKey(null,childElement.getAttribute("idref") ) ) );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( childElement.getTagName().equals("hop:parent") ) {
|
||||||
|
// add a NodeHandle to parent object
|
||||||
|
helmaNode.setParentHandle (new NodeHandle (new DbKey(null,childElement.getAttribute("idref") ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we come until here, childelement is a property value
|
||||||
|
Property prop = new Property (childElement.getTagName(), helmaNode);
|
||||||
|
if ( !"".equals(childElement.getAttribute("id")) || !"".equals(childElement.getAttribute("idref")) ) {
|
||||||
|
// we've got an object!
|
||||||
|
String idref = childElement.getAttribute("idref");
|
||||||
|
prop.setNodeHandle (new NodeHandle(new DbKey(null,idref)));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
String type = childElement.getAttribute("type");
|
||||||
|
String content = XmlUtil.getTextContent(childElement);
|
||||||
|
if ( type.equals("boolean") ) {
|
||||||
|
if ( content.equals("true") ) {
|
||||||
|
prop.setBooleanValue(true);
|
||||||
|
} else {
|
||||||
|
prop.setBooleanValue(false);
|
||||||
|
}
|
||||||
|
} else if ( type.equals("date") ) {
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat ( DATEFORMAT );
|
||||||
|
try {
|
||||||
|
Date date = format.parse(content);
|
||||||
|
prop.setDateValue (date);
|
||||||
|
} catch ( ParseException e ) {
|
||||||
|
prop.setStringValue (content);
|
||||||
|
}
|
||||||
|
} else if ( type.equals("float") ) {
|
||||||
|
prop.setFloatValue ((new Double(content)).doubleValue());
|
||||||
|
} else if ( type.equals("integer") ) {
|
||||||
|
prop.setIntegerValue ((new Long(content)).longValue());
|
||||||
|
} else {
|
||||||
|
prop.setStringValue (content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
propMap.put (childElement.getTagName(), prop);
|
||||||
|
}
|
||||||
|
if ( propMap.size()>0 )
|
||||||
|
helmaNode.setPropMap (propMap);
|
||||||
|
else
|
||||||
|
helmaNode.setPropMap (null);
|
||||||
|
if ( subnodes.size()>0 )
|
||||||
|
helmaNode.setSubnodes (subnodes);
|
||||||
|
else
|
||||||
|
helmaNode.setSubnodes (null);
|
||||||
|
return helmaNode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import javax.xml.parsers.ParserConfigurationException;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
@ -54,6 +55,24 @@ public class XmlUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get first "real" element (ie not the document-rootelement, but the next one
|
||||||
|
*/
|
||||||
|
public static Element getFirstElement (Document document) {
|
||||||
|
Element workelement = null;
|
||||||
|
if ( document.getDocumentElement()!=null ) {
|
||||||
|
org.w3c.dom.Node tmp = document.getDocumentElement().getFirstChild();
|
||||||
|
while( tmp!=null ) {
|
||||||
|
tmp = tmp.getNextSibling();
|
||||||
|
if ( tmp.getNodeType()==org.w3c.dom.Node.ELEMENT_NODE ) {
|
||||||
|
workelement = (Element) tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return workelement;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return the text content of an element
|
* return the text content of an element
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
package helma.objectmodel.dom;
|
package helma.objectmodel.dom;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.File;
|
||||||
import java.net.*;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.Date;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
import helma.objectmodel.*;
|
import helma.objectmodel.*;
|
||||||
|
import helma.objectmodel.INode;
|
||||||
|
import helma.objectmodel.IProperty;
|
||||||
|
import helma.objectmodel.TransientNode;
|
||||||
|
import helma.objectmodel.db.Node;
|
||||||
|
import helma.objectmodel.db.DbMapping;
|
||||||
import helma.util.HtmlEncoder;
|
import helma.util.HtmlEncoder;
|
||||||
|
|
||||||
public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
|
@ -19,6 +32,9 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
private StringBuffer prefix = new StringBuffer();
|
private StringBuffer prefix = new StringBuffer();
|
||||||
|
|
||||||
private static int fileid;
|
private static int fileid;
|
||||||
|
private SimpleDateFormat format = new SimpleDateFormat ( DATEFORMAT );
|
||||||
|
|
||||||
|
private boolean dbmode = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create ids that can be used for temporary files.
|
* create ids that can be used for temporary files.
|
||||||
|
@ -38,14 +54,26 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
super(out);
|
super(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlWriter (OutputStream out, String enc) throws UnsupportedEncodingException {
|
||||||
|
super(out, enc);
|
||||||
|
}
|
||||||
|
|
||||||
public XmlWriter (String desc) throws FileNotFoundException {
|
public XmlWriter (String desc) throws FileNotFoundException {
|
||||||
super (new FileOutputStream (desc));
|
super (new FileOutputStream (desc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlWriter (String desc, String enc) throws FileNotFoundException, UnsupportedEncodingException {
|
||||||
|
super (new FileOutputStream (desc), enc);
|
||||||
|
}
|
||||||
|
|
||||||
public XmlWriter (File file) throws FileNotFoundException {
|
public XmlWriter (File file) throws FileNotFoundException {
|
||||||
super (new FileOutputStream (file));
|
super (new FileOutputStream (file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlWriter (File file, String enc) throws FileNotFoundException, UnsupportedEncodingException {
|
||||||
|
super (new FileOutputStream (file), enc);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* by default writing only descends 50 levels into the node tree to prevent
|
* by default writing only descends 50 levels into the node tree to prevent
|
||||||
* infite loops. number can be changed here.
|
* infite loops. number can be changed here.
|
||||||
|
@ -54,6 +82,10 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
maxLevels = levels;
|
maxLevels = levels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDatabaseMode (boolean dbmode) {
|
||||||
|
this.dbmode = dbmode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the number of space chars
|
* set the number of space chars
|
||||||
*/
|
*/
|
||||||
|
@ -72,7 +104,8 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
*/
|
*/
|
||||||
public boolean write( INode node ) throws IOException {
|
public boolean write( INode node ) throws IOException {
|
||||||
convertedNodes = new Vector();
|
convertedNodes = new Vector();
|
||||||
writeln ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
String encoding = getEncoding();
|
||||||
|
writeln ("<?xml version=\"1.0\" encoding=\""+encoding+"\"?>");
|
||||||
writeln ("<!-- printed by helma object publisher -->");
|
writeln ("<!-- printed by helma object publisher -->");
|
||||||
writeln ("<!-- created " + (new Date()).toString() + " -->" );
|
writeln ("<!-- created " + (new Date()).toString() + " -->" );
|
||||||
write ("<xmlroot xmlns:hop=\"");
|
write ("<xmlroot xmlns:hop=\"");
|
||||||
|
@ -86,17 +119,26 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* write a hopobject and print all its properties and children.
|
* write a hopobject and print all its properties and children.
|
||||||
* if node has already been fully printed, just make a reference here.
|
* references are made here if a node already has been fully printed
|
||||||
|
* or if this is the last level that's going to be dumped
|
||||||
*/
|
*/
|
||||||
public void write (INode node, String name, int level) throws IOException {
|
public void write (INode node, String name, int level) throws IOException {
|
||||||
if ( ++level>maxLevels )
|
if (node==null)
|
||||||
return;
|
return;
|
||||||
prefix.append(indent);
|
prefix.append(indent);
|
||||||
|
if ( ++level>maxLevels ) {
|
||||||
|
writeReferenceTag (node, name);
|
||||||
|
prefix = prefix.delete( prefix.length()-indent.length(), Integer.MAX_VALUE );
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ( convertedNodes.contains(node) ) {
|
if ( convertedNodes.contains(node) ) {
|
||||||
writeReferenceTag (node, name);
|
writeReferenceTag (node, name);
|
||||||
} else {
|
} else {
|
||||||
convertedNodes.addElement (node);
|
convertedNodes.addElement (node);
|
||||||
writeTagOpen (node,name);
|
writeTagOpen (node,name);
|
||||||
|
if ( node.getParent()!=null ) {
|
||||||
|
writeReferenceTag (node.getParent(),"hop:parent");
|
||||||
|
}
|
||||||
writeProperties (node,level);
|
writeProperties (node,level);
|
||||||
writeChildren (node,level);
|
writeChildren (node,level);
|
||||||
writeTagClose (node,name);
|
writeTagClose (node,name);
|
||||||
|
@ -104,12 +146,24 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
prefix = prefix.delete( prefix.length()-indent.length(), Integer.MAX_VALUE );
|
prefix = prefix.delete( prefix.length()-indent.length(), Integer.MAX_VALUE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* loop through properties and print them with their property-name
|
* loop through properties and print them with their property-name
|
||||||
* as elementname
|
* as elementname
|
||||||
*/
|
*/
|
||||||
private void writeProperties (INode node, int level) throws IOException {
|
private void writeProperties (INode node, int level) throws IOException {
|
||||||
Enumeration e = node.properties();
|
Enumeration e = null;
|
||||||
|
if ( dbmode==true && node instanceof helma.objectmodel.db.Node ) {
|
||||||
|
// a newly constructed db.Node doesn't have a propMap,
|
||||||
|
// but returns an enumeration of all it's db-mapped properties
|
||||||
|
Hashtable props = ((Node)node).getPropMap();
|
||||||
|
if (props==null)
|
||||||
|
return;
|
||||||
|
e = props.keys();
|
||||||
|
} else {
|
||||||
|
e = node.properties();
|
||||||
|
}
|
||||||
while ( e.hasMoreElements() ) {
|
while ( e.hasMoreElements() ) {
|
||||||
String key = (String)e.nextElement();
|
String key = (String)e.nextElement();
|
||||||
IProperty prop = node.get(key,false);
|
IProperty prop = node.get(key,false);
|
||||||
|
@ -129,7 +183,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
write (indent);
|
write (indent);
|
||||||
write ("<");
|
write ("<");
|
||||||
write (key);
|
write (key);
|
||||||
write (" hop:type=\"null\"/>");
|
write (" type=\"null\"/>");
|
||||||
write (LINESEPARATOR);
|
write (LINESEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,18 +198,17 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
write (property.getName());
|
write (property.getName());
|
||||||
switch (property.getType()) {
|
switch (property.getType()) {
|
||||||
case IProperty.BOOLEAN:
|
case IProperty.BOOLEAN:
|
||||||
write (" hop:type=\"boolean\"");
|
write (" type=\"boolean\"");
|
||||||
break;
|
break;
|
||||||
case IProperty.FLOAT:
|
case IProperty.FLOAT:
|
||||||
write (" hop:type=\"float\"");
|
write (" type=\"float\"");
|
||||||
break;
|
break;
|
||||||
case IProperty.INTEGER:
|
case IProperty.INTEGER:
|
||||||
write (" hop:type=\"integer\"");
|
write (" type=\"integer\"");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( property.getType()==IProperty.DATE ) {
|
if ( property.getType()==IProperty.DATE ) {
|
||||||
write (" hop:type=\"date\"");
|
write (" type=\"date\"");
|
||||||
SimpleDateFormat format = new SimpleDateFormat ( DATEFORMAT );
|
|
||||||
write (">");
|
write (">");
|
||||||
write ( format.format (property.getDateValue()) );
|
write ( format.format (property.getDateValue()) );
|
||||||
} else {
|
} else {
|
||||||
|
@ -172,6 +225,12 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
* loop through the children-array and print them as <hop:child>
|
* loop through the children-array and print them as <hop:child>
|
||||||
*/
|
*/
|
||||||
private void writeChildren (INode node, int level) throws IOException {
|
private void writeChildren (INode node, int level) throws IOException {
|
||||||
|
if ( dbmode==true && node instanceof helma.objectmodel.db.Node ) {
|
||||||
|
Node dbNode = (Node)node;
|
||||||
|
DbMapping smap = dbNode.getDbMapping() == null ? null : dbNode.getDbMapping().getSubnodeMapping ();
|
||||||
|
if (smap != null && smap.isRelational ())
|
||||||
|
return;
|
||||||
|
}
|
||||||
Enumeration e = node.getSubnodes();
|
Enumeration e = node.getSubnodes();
|
||||||
while (e.hasMoreElements()) {
|
while (e.hasMoreElements()) {
|
||||||
INode nextNode = (INode)e.nextElement();
|
INode nextNode = (INode)e.nextElement();
|
||||||
|
@ -187,12 +246,18 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
write (prefix.toString());
|
write (prefix.toString());
|
||||||
write ("<");
|
write ("<");
|
||||||
write ( (name==null)?"hopobject" : name);
|
write ( (name==null)?"hopobject" : name);
|
||||||
write (" hop:id=\"");
|
write (" id=\"");
|
||||||
write (getNodeIdentifier(node));
|
write (getNodeIdentifier(node));
|
||||||
write ("\" hop:prototype=\"");
|
write ("\" name=\"");
|
||||||
|
write (node.getName());
|
||||||
|
write ("\" prototype=\"");
|
||||||
write (getNodePrototype(node));
|
write (getNodePrototype(node));
|
||||||
write ("\"");
|
write ("\" created=\"");
|
||||||
write (">");
|
write (Long.toString(node.created()));
|
||||||
|
write ("\" lastModified=\"");
|
||||||
|
write (Long.toString(node.lastModified()));
|
||||||
|
//FIXME: do we need anonymous-property?
|
||||||
|
write ("\">");
|
||||||
write (LINESEPARATOR);
|
write (LINESEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,16 +275,16 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* write a tag holding a reference to an element that has
|
* write a tag holding a reference to an element that has
|
||||||
* been dumped before.
|
* been written out before.
|
||||||
* e.g. <parent hop:idref="t35" hop:prototyperef="hopobject"/>
|
* e.g. <parent idref="35" prototyperef="hopobject"/>
|
||||||
*/
|
*/
|
||||||
public void writeReferenceTag (INode node, String name) throws IOException {
|
public void writeReferenceTag (INode node, String name) throws IOException {
|
||||||
write (prefix.toString());
|
write (prefix.toString());
|
||||||
write ("<");
|
write ("<");
|
||||||
write ( (name==null)?"hopobject" : name);
|
write ( (name==null)?"hopobject" : name);
|
||||||
write ( " hop:idref=\"");
|
write ( " idref=\"");
|
||||||
write (getNodeIdentifier(node));
|
write (getNodeIdentifier(node));
|
||||||
write ("\" hop:prototyperef=\"");
|
write ("\" prototyperef=\"");
|
||||||
write (getNodePrototype(node));
|
write (getNodePrototype(node));
|
||||||
write ("\"");
|
write ("\"");
|
||||||
write ("/>");
|
write ("/>");
|
||||||
|
|
Loading…
Add table
Reference in a new issue