Merging changes from 1.2.4 to 1.2.5
This commit is contained in:
parent
65e4db3d8a
commit
676b70519d
32 changed files with 1498 additions and 865 deletions
|
@ -1,10 +1,13 @@
|
||||||
# List of apps to start.
|
# List of apps to start.
|
||||||
|
|
||||||
|
# mount antville as root application
|
||||||
|
antville
|
||||||
|
antville.mountpoint = /
|
||||||
|
antville.static = static
|
||||||
|
antville.staticMountpoint = /static
|
||||||
|
|
||||||
# mount antville as /managehop to avoid
|
# mount antville as /managehop to avoid
|
||||||
# conflict with antville's manage.hac action
|
# conflict with antville's manage.hac action
|
||||||
manage
|
manage
|
||||||
manage.mountpoint = /manage/hop
|
manage.mountpoint = /manage/hop
|
||||||
|
|
||||||
# mount antville as root application
|
|
||||||
antville
|
|
||||||
antville.mountpoint = /
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<target name="init">
|
<target name="init">
|
||||||
<property name="Name" value="helma"/>
|
<property name="Name" value="helma"/>
|
||||||
<property name="year" value="1998-${year}"/>
|
<property name="year" value="1998-${year}"/>
|
||||||
<property name="version" value="1.3cvs"/>
|
<property name="version" value="1.2.5"/>
|
||||||
<property name="project" value="helma"/>
|
<property name="project" value="helma"/>
|
||||||
<property name="build.compiler" value="classic"/>
|
<property name="build.compiler" value="classic"/>
|
||||||
|
|
||||||
|
@ -28,8 +28,7 @@
|
||||||
|
|
||||||
<property name="jar.name" value="${project}"/>
|
<property name="jar.name" value="${project}"/>
|
||||||
<property name="package.name" value="${project}-${version}"/>
|
<property name="package.name" value="${project}-${version}"/>
|
||||||
<property name="antclick.name" value="antclick-1.0cvs"/>
|
<property name="antclick.name" value="antclick-1.0.1-pre1"/>
|
||||||
<property name="antville.name" value="antville-1.0cvs"/>
|
|
||||||
|
|
||||||
<property name="debug" value="on"/>
|
<property name="debug" value="on"/>
|
||||||
<property name="optimize" value="on"/>
|
<property name="optimize" value="on"/>
|
||||||
|
@ -68,8 +67,6 @@
|
||||||
<echo message=" docs --> tries to retrieve the HTML documentation "/>
|
<echo message=" docs --> tries to retrieve the HTML documentation "/>
|
||||||
<echo message=" (may need proxy settings in startscript)"/>
|
<echo message=" (may need proxy settings in startscript)"/>
|
||||||
<echo message=" package --> generates the distribution (zip and tar.gz)"/>
|
<echo message=" package --> generates the distribution (zip and tar.gz)"/>
|
||||||
<echo message=" antville --> generates the antville-distribution"/>
|
|
||||||
<echo message=" (zip and tar.gz)"/>
|
|
||||||
<echo message=" antclick --> generates the distribution (zip and tar.gz)"/>
|
<echo message=" antclick --> generates the distribution (zip and tar.gz)"/>
|
||||||
<echo message=" with antville preconfigured"/>
|
<echo message=" with antville preconfigured"/>
|
||||||
<echo message=" app [name] --> gets an application from the cvs and zips it"/>
|
<echo message=" app [name] --> gets an application from the cvs and zips it"/>
|
||||||
|
@ -210,8 +207,8 @@
|
||||||
<!-- copy all libraries except helma-YYYYMMDD.jar -->
|
<!-- copy all libraries except helma-YYYYMMDD.jar -->
|
||||||
<copy todir="${build.work}/lib">
|
<copy todir="${build.work}/lib">
|
||||||
<fileset dir="${home.dir}/lib">
|
<fileset dir="${home.dir}/lib">
|
||||||
<exclude name="**/helma*.jar" />
|
<exclude name="helma*.jar" />
|
||||||
<include name="**/*.jar" />
|
<include name="*.jar" />
|
||||||
</fileset>
|
</fileset>
|
||||||
</copy>
|
</copy>
|
||||||
|
|
||||||
|
@ -251,17 +248,6 @@
|
||||||
|
|
||||||
<antcall target="package-manage" />
|
<antcall target="package-manage" />
|
||||||
|
|
||||||
<!-- write out apps.properties file -->
|
|
||||||
<echo file="${build.work}/apps.properties" append="false">
|
|
||||||
# list of applications to be started by helma
|
|
||||||
base
|
|
||||||
base.mountpoint = /
|
|
||||||
bloggerapi
|
|
||||||
himp
|
|
||||||
gong
|
|
||||||
lillebror
|
|
||||||
manage
|
|
||||||
</echo>
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
|
||||||
|
@ -360,14 +346,16 @@ manage
|
||||||
|
|
||||||
<!-- unzip images -->
|
<!-- unzip images -->
|
||||||
<mkdir dir="${build.work}/static/images"/>
|
<mkdir dir="${build.work}/static/images"/>
|
||||||
<unzip src="${build.work}/apps/antville/images.zip" dest="${build.work}/static/images" />
|
<unzip src="${build.work}/apps/antville/images.zip" dest="${build.work}/static/images">
|
||||||
|
<patternset>
|
||||||
|
<include name="**"/>
|
||||||
|
</patternset>
|
||||||
|
</unzip>
|
||||||
|
|
||||||
<!-- zip db_support directory and remove it afterwards -->
|
<!-- delete antville's mysql-scripts, image-zip etc -->
|
||||||
<zip zipfile="${build.work}/apps/antville/db_support.zip">
|
<delete>
|
||||||
<zipfileset dir="${build.work}/apps/antville/db_support" prefix="db_support" includes="**" />
|
<fileset dir="${build.work}/apps/antville" includes="images.zip,*.sql" />
|
||||||
</zip>
|
</delete>
|
||||||
<delete dir="${build.work}/apps/antville/db_support" />
|
|
||||||
<delete file="${build.work}/apps/antville/images.zip" />
|
|
||||||
|
|
||||||
<!-- get and zip manage-app -->
|
<!-- get and zip manage-app -->
|
||||||
<antcall target="package-manage" />
|
<antcall target="package-manage" />
|
||||||
|
@ -411,36 +399,4 @@ manage
|
||||||
<delete dir="${build.work}" />
|
<delete dir="${build.work}" />
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- =================================================================== -->
|
|
||||||
<!-- Gets antville from the cvs, cleans and zips/targzs it -->
|
|
||||||
<!-- =================================================================== -->
|
|
||||||
<target name="antville" depends="init">
|
|
||||||
<mkdir dir="${build.dist}" />
|
|
||||||
<mkdir dir="${build.work}" />
|
|
||||||
|
|
||||||
<!-- to retrieve special versions of an application insert
|
|
||||||
additional attributes: tag="TAGNAME" or date="1972-09-24 20:05" -->
|
|
||||||
<cvs cvsRoot="${cvs.root.apps}" command="export" tag="HEAD" package="antville" dest="${build.work}" />
|
|
||||||
|
|
||||||
<!-- zip db_support directory and remove it afterwards -->
|
|
||||||
<zip zipfile="${build.work}/antville/db_support.zip">
|
|
||||||
<zipfileset dir="${build.work}/antville/db_support" prefix="db_support" includes="**" />
|
|
||||||
</zip>
|
|
||||||
<delete dir="${build.work}/antville/db_support" />
|
|
||||||
|
|
||||||
<fixcrlf srcdir="${build.work}/antville/" eol="crlf" excludes="**/*.zip" />
|
|
||||||
<zip zipfile="${build.dist}/${antville.name}.zip" basedir="${build.work}" includes="**" />
|
|
||||||
|
|
||||||
<fixcrlf srcdir="${build.work}/antville/" eol="lf" eof="remove" excludes="**/*.zip" />
|
|
||||||
<tar tarfile="${build.dist}/${antville.name}.tar" basedir="${build.work}">
|
|
||||||
<tarfileset dir="${build.work}">
|
|
||||||
<include name="${build.work}/**"/>
|
|
||||||
</tarfileset>
|
|
||||||
</tar>
|
|
||||||
<gzip zipfile="${build.dist}/${antville.name}.tar.gz" src="${build.dist}/${antville.name}.tar" />
|
|
||||||
|
|
||||||
<delete file="${build.dist}/${antville.name}.tar"/>
|
|
||||||
<delete dir="${build.work}" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
# List of apps to start.
|
# List of applications to start.
|
||||||
|
|
||||||
base
|
base
|
||||||
base.mountpoint = /
|
base.mountpoint = /
|
||||||
|
base.static = static
|
||||||
|
base.staticMountpoint = /static
|
||||||
|
|
||||||
manage
|
manage
|
||||||
|
|
||||||
|
gong
|
||||||
himp
|
himp
|
||||||
bloggerapi
|
bloggerapi
|
||||||
lillebror
|
lillebror
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
||||||
<!-- printed by helma object publisher -->
|
|
||||||
<!-- created Fri May 31 16:36:44 CEST 2002 -->
|
|
||||||
<xmlroot xmlns:hop="http://www.helma.org/docs/guide/features/database">
|
|
||||||
<hopobject id="0" name="root" prototype="root" created="1022855750998" lastModified="1022855804064">
|
|
||||||
<hop:child idref="3" prototyperef="weblogstory"/>
|
|
||||||
</hopobject>
|
|
||||||
</xmlroot>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
||||||
<!-- printed by helma object publisher -->
|
|
||||||
<!-- created Fri May 31 16:36:30 CEST 2002 -->
|
|
||||||
<xmlroot xmlns:hop="http://www.helma.org/docs/guide/features/database">
|
|
||||||
<hopobject id="1" name="users" prototype="hopobject" created="1022855751010" lastModified="1022855790024">
|
|
||||||
<helma idref="2" prototyperef="user"/>
|
|
||||||
</hopobject>
|
|
||||||
</xmlroot>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
||||||
<!-- printed by helma object publisher -->
|
|
||||||
<!-- created Fri May 31 16:36:30 CEST 2002 -->
|
|
||||||
<xmlroot xmlns:hop="http://www.helma.org/docs/guide/features/database">
|
|
||||||
<hopobject id="2" name="helma" prototype="user" created="1022855790020" lastModified="1022855790025">
|
|
||||||
<hop:parent idref="1" prototyperef="hopobject"/>
|
|
||||||
<email>admin@somedomain.at</email>
|
|
||||||
<password>helma</password>
|
|
||||||
<name>helma</name>
|
|
||||||
</hopobject>
|
|
||||||
</xmlroot>
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
||||||
<!-- printed by helma object publisher -->
|
|
||||||
<!-- created Fri May 31 16:36:44 CEST 2002 -->
|
|
||||||
<xmlroot xmlns:hop="http://www.helma.org/docs/guide/features/database">
|
|
||||||
<hopobject id="3" name="weblogstory" prototype="weblogstory" created="1022855804062" lastModified="1022855804064">
|
|
||||||
<hop:parent idref="0" prototyperef="root"/>
|
|
||||||
<postdate type="date">31.05.2002 16:36:44 CEST</postdate>
|
|
||||||
<day>2002.05.31</day>
|
|
||||||
<author idref="2" prototyperef="user"/>
|
|
||||||
<text>Congratulations! You successfully created your Helma HopBlog!
|
|
||||||
|
|
||||||
As a first step you can login to your HopBlog using "helma" as user name and password (certainly without the quotes) and create or edit stories.
|
|
||||||
|
|
||||||
Or you set-up HopBlog administration for yourself. You can do this by opening the file app.properties in the apps/hopblog directory of your Helma installation and editing the settings for siteAdmin, adminEmail and smtp.
|
|
||||||
|
|
||||||
After that you should register the new administrator as HopBlog user. Simply enter the data you chose for siteAdmin and adminEmail in the appropriate fields of the registration form, fill in the other form fields and submit your data. If you have set a valid e-mail address and smtp server, you should get a message confirming the registration.
|
|
||||||
|
|
||||||
Now you can login to your HopBlog using name and password of the newly created user.
|
|
||||||
|
|
||||||
Let the fun begin...
|
|
||||||
:)</text>
|
|
||||||
<moddate type="date">31.05.2002 16:36:44 CEST</moddate>
|
|
||||||
</hopobject>
|
|
||||||
</xmlroot>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
||||||
<!-- printed by helma object publisher -->
|
|
||||||
<!-- created Fri May 31 16:36:44 CEST 2002 -->
|
|
||||||
<xmlroot>
|
|
||||||
<counter>3</counter>
|
|
||||||
</xmlroot>
|
|
BIN
build/main/lib/ext/mysql.jar
Normal file
BIN
build/main/lib/ext/mysql.jar
Normal file
Binary file not shown.
|
@ -410,6 +410,12 @@ public abstract class ESLoader extends ESObject {
|
||||||
// The simplest case is direct object compatibility
|
// The simplest case is direct object compatibility
|
||||||
sourceClass = params[i].getClass();
|
sourceClass = params[i].getClass();
|
||||||
accepted = targetClass.isAssignableFrom(sourceClass);
|
accepted = targetClass.isAssignableFrom(sourceClass);
|
||||||
|
if (targetClass != sourceClass) {
|
||||||
|
if (targetClass == Object.class)
|
||||||
|
distance += 2;
|
||||||
|
else
|
||||||
|
distance += 1;
|
||||||
|
}
|
||||||
debugInfo = " accepted (subclassing)";
|
debugInfo = " accepted (subclassing)";
|
||||||
|
|
||||||
if (!accepted) {
|
if (!accepted) {
|
||||||
|
@ -440,6 +446,7 @@ public abstract class ESLoader extends ESObject {
|
||||||
convertToChar = new boolean[n];
|
convertToChar = new boolean[n];
|
||||||
}
|
}
|
||||||
convertToChar[i] = true;
|
convertToChar[i] = true;
|
||||||
|
distance += 1;
|
||||||
debugInfo = " accepted (String(1) as Character)";
|
debugInfo = " accepted (String(1) as Character)";
|
||||||
} else {
|
} else {
|
||||||
debugInfo = " rejected (String not of length 1)";
|
debugInfo = " rejected (String not of length 1)";
|
||||||
|
|
|
@ -77,12 +77,6 @@ public final class Application implements IPathElement, Runnable {
|
||||||
*/
|
*/
|
||||||
protected SkinManager skinmgr;
|
protected SkinManager skinmgr;
|
||||||
|
|
||||||
/**
|
|
||||||
* Each application has one internal request evaluator for calling
|
|
||||||
* the scheduler and other internal functions.
|
|
||||||
*/
|
|
||||||
RequestEvaluator eval;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collections for evaluator thread pooling
|
* Collections for evaluator thread pooling
|
||||||
*/
|
*/
|
||||||
|
@ -283,24 +277,25 @@ public final class Application implements IPathElement, Runnable {
|
||||||
loadSessionData(null);
|
loadSessionData(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create and init type mananger
|
||||||
typemgr = new TypeManager(this);
|
typemgr = new TypeManager(this);
|
||||||
typemgr.createPrototypes();
|
typemgr.createPrototypes();
|
||||||
|
|
||||||
// logEvent ("Started type manager for "+name);
|
// create and init evaluator/thread lists
|
||||||
// eval = new RequestEvaluator (this);
|
|
||||||
logEvent("Starting evaluators for " + name);
|
|
||||||
freeThreads = new Stack();
|
freeThreads = new Stack();
|
||||||
allThreads = new Vector();
|
allThreads = new Vector();
|
||||||
|
|
||||||
// allThreads.addElement (eval);
|
|
||||||
// preallocate minThreads request evaluators
|
// preallocate minThreads request evaluators
|
||||||
int minThreads = 0;
|
int minThreads = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
minThreads = Integer.parseInt(props.getProperty("minThreads"));
|
minThreads = Integer.parseInt(props.getProperty("minThreads"));
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
|
// not parsable as number, keep 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logEvent("Starting "+minThreads+" evaluator(s) for " + name);
|
||||||
|
|
||||||
for (int i = 0; i < minThreads; i++) {
|
for (int i = 0; i < minThreads; i++) {
|
||||||
RequestEvaluator ev = new RequestEvaluator(this);
|
RequestEvaluator ev = new RequestEvaluator(this);
|
||||||
|
|
||||||
|
@ -339,7 +334,7 @@ public final class Application implements IPathElement, Runnable {
|
||||||
public void start() {
|
public void start() {
|
||||||
starttime = System.currentTimeMillis();
|
starttime = System.currentTimeMillis();
|
||||||
worker = new Thread(this, "Worker-" + name);
|
worker = new Thread(this, "Worker-" + name);
|
||||||
worker.setPriority(Thread.NORM_PRIORITY + 2);
|
// worker.setPriority(Thread.NORM_PRIORITY + 2);
|
||||||
worker.start();
|
worker.start();
|
||||||
|
|
||||||
// logEvent ("session cleanup and scheduler thread started");
|
// logEvent ("session cleanup and scheduler thread started");
|
||||||
|
@ -740,6 +735,13 @@ public final class Application implements IPathElement, Runnable {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the prototype with the given name, if it exists
|
||||||
|
*/
|
||||||
|
public Prototype getPrototypeByName(String name) {
|
||||||
|
return (Prototype) typemgr.prototypes.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a collection containing all prototypes defined for this application
|
* Return a collection containing all prototypes defined for this application
|
||||||
*/
|
*/
|
||||||
|
@ -1101,17 +1103,7 @@ public final class Application implements IPathElement, Runnable {
|
||||||
* by an active RequestEvaluator thread.
|
* by an active RequestEvaluator thread.
|
||||||
*/
|
*/
|
||||||
private Object invokeFunction(Object obj, String func, Object[] args) {
|
private Object invokeFunction(Object obj, String func, Object[] args) {
|
||||||
Thread thread = Thread.currentThread();
|
RequestEvaluator reval = getCurrentRequestEvaluator();
|
||||||
RequestEvaluator reval = null;
|
|
||||||
int l = allThreads.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < l; i++) {
|
|
||||||
RequestEvaluator r = (RequestEvaluator) allThreads.get(i);
|
|
||||||
|
|
||||||
if ((r != null) && (r.rtx == thread)) {
|
|
||||||
reval = r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reval != null) {
|
if (reval != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -1225,8 +1217,8 @@ public final class Application implements IPathElement, Runnable {
|
||||||
// we use the classes from helma.doc-pacakge for introspection.
|
// we use the classes from helma.doc-pacakge for introspection.
|
||||||
// the first time an url like /appname/api/ is parsed, the application is read again
|
// the first time an url like /appname/api/ is parsed, the application is read again
|
||||||
// parsed for comments and exposed as an IPathElement
|
// parsed for comments and exposed as an IPathElement
|
||||||
if (name.equals("api")) {
|
if (name.equals("api") && allThreads.size() > 0) {
|
||||||
return eval.scriptingEngine.getIntrospector();
|
return ((RequestEvaluator) allThreads.get(0)).scriptingEngine.getIntrospector();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -1306,27 +1298,37 @@ public final class Application implements IPathElement, Runnable {
|
||||||
* kicking out expired user sessions.
|
* kicking out expired user sessions.
|
||||||
*/
|
*/
|
||||||
public void run() {
|
public void run() {
|
||||||
long cleanupSleep = 60000; // thread sleep interval (fixed)
|
// interval between session cleanups
|
||||||
long scheduleSleep = 60000; // interval for scheduler invocation
|
long sessionCleanupInterval = 60000;
|
||||||
long lastScheduler = 0; // run scheduler immediately
|
long lastSessionCleanup = System.currentTimeMillis();
|
||||||
long lastCleanup = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// logEvent ("Starting scheduler for "+name);
|
// logEvent ("Starting scheduler for "+name);
|
||||||
// as first thing, invoke function onStart in the root object
|
|
||||||
eval = new RequestEvaluator(this);
|
|
||||||
allThreads.addElement(eval);
|
|
||||||
|
|
||||||
// read in standard prototypes to make first request go faster
|
// read in standard prototypes to make first request go faster
|
||||||
typemgr.updatePrototype("root");
|
typemgr.updatePrototype("root");
|
||||||
typemgr.updatePrototype("global");
|
typemgr.updatePrototype("global");
|
||||||
|
|
||||||
|
// as first thing, invoke function onStart in the root object
|
||||||
|
RequestEvaluator eval = getEvaluator();
|
||||||
try {
|
try {
|
||||||
eval.invokeFunction((INode) null, "onStart", new Object[0]);
|
eval.invokeFunction((INode) null, "onStart", new Object[0]);
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
logEvent("Error in " + name + "/onStart(): " + ignore);
|
logEvent("Error in " + name + "/onStart(): " + ignore);
|
||||||
|
} finally {
|
||||||
|
if (!stopped) {
|
||||||
|
releaseEvaluator(eval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (Thread.currentThread() == worker) {
|
while (Thread.currentThread() == worker) {
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// check if we should clean up user sessions
|
||||||
|
if ((now - lastSessionCleanup) > sessionCleanupInterval) {
|
||||||
|
|
||||||
|
lastSessionCleanup = now;
|
||||||
|
|
||||||
// get session timeout
|
// get session timeout
|
||||||
int sessionTimeout = 30;
|
int sessionTimeout = 30;
|
||||||
|
|
||||||
|
@ -1334,16 +1336,9 @@ public final class Application implements IPathElement, Runnable {
|
||||||
sessionTimeout = Math.max(0,
|
sessionTimeout = Math.max(0,
|
||||||
Integer.parseInt(props.getProperty("sessionTimeout",
|
Integer.parseInt(props.getProperty("sessionTimeout",
|
||||||
"30")));
|
"30")));
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {}
|
||||||
System.out.println(ignore.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// check if we should clean up user sessions
|
|
||||||
if ((now - lastCleanup) > cleanupSleep) {
|
|
||||||
try {
|
try {
|
||||||
lastCleanup = now;
|
|
||||||
|
|
||||||
Hashtable cloned = (Hashtable) sessions.clone();
|
Hashtable cloned = (Hashtable) sessions.clone();
|
||||||
|
|
||||||
|
@ -1398,7 +1393,7 @@ public final class Application implements IPathElement, Runnable {
|
||||||
try {
|
try {
|
||||||
thisEvaluator = getEvaluator();
|
thisEvaluator = getEvaluator();
|
||||||
} catch (RuntimeException rt) {
|
} catch (RuntimeException rt) {
|
||||||
if (stopped == false) {
|
if (!stopped) {
|
||||||
logEvent("couldn't execute " + j +
|
logEvent("couldn't execute " + j +
|
||||||
", maximum thread count reached");
|
", maximum thread count reached");
|
||||||
|
|
||||||
|
@ -1414,8 +1409,8 @@ public final class Application implements IPathElement, Runnable {
|
||||||
(CronJob.millisToNextFullMinute() < 30000)) {
|
(CronJob.millisToNextFullMinute() < 30000)) {
|
||||||
CronRunner r = new CronRunner(thisEvaluator, j);
|
CronRunner r = new CronRunner(thisEvaluator, j);
|
||||||
|
|
||||||
r.start();
|
|
||||||
activeCronJobs.put(j.getName(), r);
|
activeCronJobs.put(j.getName(), r);
|
||||||
|
r.start();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
thisEvaluator.invokeFunction((INode) null, j.getFunction(),
|
thisEvaluator.invokeFunction((INode) null, j.getFunction(),
|
||||||
|
@ -1423,19 +1418,23 @@ public final class Application implements IPathElement, Runnable {
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logEvent("error running " + j + ": " + ex.toString());
|
logEvent("error running " + j + ": " + ex.toString());
|
||||||
} finally {
|
} finally {
|
||||||
if (stopped == false) {
|
if (!stopped) {
|
||||||
releaseEvaluator(thisEvaluator);
|
releaseEvaluator(thisEvaluator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
thisEvaluator = null;
|
|
||||||
}
|
long sleepInterval = CronJob.millisToNextFullMinute();
|
||||||
}
|
try {
|
||||||
|
sleepInterval = Integer.parseInt(props.getProperty("schedulerInterval"))*1000;
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
|
||||||
// sleep until the next full minute
|
// sleep until the next full minute
|
||||||
try {
|
try {
|
||||||
worker.sleep(CronJob.millisToNextFullMinute());
|
worker.sleep(sleepInterval);
|
||||||
} catch (InterruptedException x) {
|
} catch (InterruptedException x) {
|
||||||
logEvent("Scheduler for " + name + " interrupted");
|
logEvent("Scheduler for " + name + " interrupted");
|
||||||
worker = null;
|
worker = null;
|
||||||
|
@ -1645,14 +1644,14 @@ public final class Application implements IPathElement, Runnable {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public int countThreads() {
|
public int countThreads() {
|
||||||
return threadgroup.activeCount() - 1;
|
return threadgroup.activeCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public int countEvaluators() {
|
public int countEvaluators() {
|
||||||
return allThreads.size() - 1;
|
return allThreads.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1666,7 +1665,7 @@ public final class Application implements IPathElement, Runnable {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public int countActiveEvaluators() {
|
public int countActiveEvaluators() {
|
||||||
return allThreads.size() - freeThreads.size() - 1;
|
return allThreads.size() - freeThreads.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1838,14 +1837,14 @@ public final class Application implements IPathElement, Runnable {
|
||||||
thisEvaluator.invokeFunction((INode) null, job.getFunction(),
|
thisEvaluator.invokeFunction((INode) null, job.getFunction(),
|
||||||
new Object[0], job.getTimeout());
|
new Object[0], job.getTimeout());
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
}
|
// gets logged in RequestEvaluator
|
||||||
|
} finally {
|
||||||
if (stopped == false) {
|
if (!stopped) {
|
||||||
releaseEvaluator(thisEvaluator);
|
releaseEvaluator(thisEvaluator);
|
||||||
}
|
}
|
||||||
|
|
||||||
thisEvaluator = null;
|
thisEvaluator = null;
|
||||||
activeCronJobs.remove(job.getName());
|
activeCronJobs.remove(job.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ public final class Prototype {
|
||||||
public long getChecksum() {
|
public long getChecksum() {
|
||||||
// long start = System.currentTimeMillis();
|
// long start = System.currentTimeMillis();
|
||||||
File[] f = getFiles();
|
File[] f = getFiles();
|
||||||
long c = 0;
|
long c = directory.lastModified();
|
||||||
|
|
||||||
for (int i = 0; i < f.length; i++)
|
for (int i = 0; i < f.length; i++)
|
||||||
c += f[i].lastModified();
|
c += f[i].lastModified();
|
||||||
|
|
|
@ -392,6 +392,11 @@ public final class RequestEvaluator implements Runnable {
|
||||||
app.logEvent("Exception in " +
|
app.logEvent("Exception in " +
|
||||||
Thread.currentThread() + ": " + x);
|
Thread.currentThread() + ": " + x);
|
||||||
|
|
||||||
|
// Dump the profiling data to System.err
|
||||||
|
if (app.debug && !(x instanceof ScriptingException)) {
|
||||||
|
x.printStackTrace ();
|
||||||
|
}
|
||||||
|
|
||||||
// set done to false so that the error will be processed
|
// set done to false so that the error will be processed
|
||||||
done = false;
|
done = false;
|
||||||
error = x.getMessage();
|
error = x.getMessage();
|
||||||
|
|
|
@ -166,7 +166,7 @@ public final class TypeManager {
|
||||||
if (zipped == null) {
|
if (zipped == null) {
|
||||||
File f = new File(appDir, list[i]);
|
File f = new File(appDir, list[i]);
|
||||||
|
|
||||||
if (!f.isDirectory()) {
|
if (!f.isDirectory() && f.exists()) {
|
||||||
zipped = new ZippedAppFile(f, app);
|
zipped = new ZippedAppFile(f, app);
|
||||||
zipfiles.put(list[i], zipped);
|
zipfiles.put(list[i], zipped);
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,8 @@ public final class TypeManager {
|
||||||
if ((dbmap != null) && dbmap.needsUpdate()) {
|
if ((dbmap != null) && dbmap.needsUpdate()) {
|
||||||
dbmap.update();
|
dbmap.update();
|
||||||
|
|
||||||
if ((proto != hopobjectProto) && (proto != globalProto)) {
|
// this is now done in dbmap.update()!!!
|
||||||
|
/*if ((proto != hopobjectProto) && (proto != globalProto)) {
|
||||||
// set parent prototype, in case it has changed.
|
// set parent prototype, in case it has changed.
|
||||||
String parentName = dbmap.getExtends();
|
String parentName = dbmap.getExtends();
|
||||||
|
|
||||||
|
@ -246,7 +247,7 @@ public final class TypeManager {
|
||||||
} else if (!app.isJavaPrototype(proto.getName())) {
|
} else if (!app.isJavaPrototype(proto.getName())) {
|
||||||
proto.setParentPrototype(hopobjectProto);
|
proto.setParentPrototype(hopobjectProto);
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,17 @@ public class ZippedAppFile implements Updatable {
|
||||||
String ename = entry.getName();
|
String ename = entry.getName();
|
||||||
StringTokenizer st = new StringTokenizer(ename, "/");
|
StringTokenizer st = new StringTokenizer(ename, "/");
|
||||||
|
|
||||||
if (st.countTokens() == 2) {
|
int tokens = st.countTokens();
|
||||||
|
if (tokens == 1) {
|
||||||
|
String fname = st.nextToken();
|
||||||
|
|
||||||
|
if ("app.properties".equalsIgnoreCase(fname)) {
|
||||||
|
app.props.addProps(file.getName(), zip.getInputStream(entry));
|
||||||
|
} else if ("db.properties".equalsIgnoreCase(fname)) {
|
||||||
|
app.dbProps.addProps(file.getName(), zip.getInputStream(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (tokens == 2) {
|
||||||
String dir = st.nextToken();
|
String dir = st.nextToken();
|
||||||
String fname = st.nextToken();
|
String fname = st.nextToken();
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import helma.framework.core.*;
|
||||||
import helma.objectmodel.*;
|
import helma.objectmodel.*;
|
||||||
import helma.servlet.*;
|
import helma.servlet.*;
|
||||||
import helma.util.SystemProperties;
|
import helma.util.SystemProperties;
|
||||||
|
import helma.util.StringUtils;
|
||||||
import org.apache.xmlrpc.XmlRpcHandler;
|
import org.apache.xmlrpc.XmlRpcHandler;
|
||||||
import org.mortbay.http.*;
|
import org.mortbay.http.*;
|
||||||
import org.mortbay.http.handler.*;
|
import org.mortbay.http.handler.*;
|
||||||
|
@ -28,7 +29,6 @@ import org.mortbay.jetty.servlet.*;
|
||||||
import org.mortbay.util.*;
|
import org.mortbay.util.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.rmi.*;
|
import java.rmi.*;
|
||||||
import java.rmi.server.*;
|
import java.rmi.server.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -38,9 +38,9 @@ import javax.servlet.Servlet;
|
||||||
* This class is responsible for starting and stopping Helma applications.
|
* This class is responsible for starting and stopping Helma applications.
|
||||||
*/
|
*/
|
||||||
public class ApplicationManager implements XmlRpcHandler {
|
public class ApplicationManager implements XmlRpcHandler {
|
||||||
|
private Hashtable descriptors;
|
||||||
private Hashtable applications;
|
private Hashtable applications;
|
||||||
private Hashtable xmlrpcHandlers;
|
private Hashtable xmlrpcHandlers;
|
||||||
private Properties mountpoints;
|
|
||||||
private int port;
|
private int port;
|
||||||
private File hopHome;
|
private File hopHome;
|
||||||
private SystemProperties props;
|
private SystemProperties props;
|
||||||
|
@ -50,10 +50,10 @@ public class ApplicationManager implements XmlRpcHandler {
|
||||||
/**
|
/**
|
||||||
* Creates a new ApplicationManager object.
|
* Creates a new ApplicationManager object.
|
||||||
*
|
*
|
||||||
* @param port ...
|
* @param port The RMI port we're binding to
|
||||||
* @param hopHome ...
|
* @param hopHome The Helma home directory
|
||||||
* @param props ...
|
* @param props the properties defining the running apps
|
||||||
* @param server ...
|
* @param server the server instance
|
||||||
*/
|
*/
|
||||||
public ApplicationManager(int port, File hopHome, SystemProperties props,
|
public ApplicationManager(int port, File hopHome, SystemProperties props,
|
||||||
Server server) {
|
Server server) {
|
||||||
|
@ -61,13 +61,16 @@ public class ApplicationManager implements XmlRpcHandler {
|
||||||
this.hopHome = hopHome;
|
this.hopHome = hopHome;
|
||||||
this.props = props;
|
this.props = props;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
|
descriptors = new Hashtable();
|
||||||
applications = new Hashtable();
|
applications = new Hashtable();
|
||||||
xmlrpcHandlers = new Hashtable();
|
xmlrpcHandlers = new Hashtable();
|
||||||
mountpoints = new Properties();
|
|
||||||
lastModified = 0;
|
lastModified = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// regularely check applications property file to create and start new applications
|
/**
|
||||||
|
* Called regularely check applications property file
|
||||||
|
* to create and start new applications.
|
||||||
|
*/
|
||||||
protected void checkForChanges() {
|
protected void checkForChanges() {
|
||||||
if (props.lastModified() > lastModified) {
|
if (props.lastModified() > lastModified) {
|
||||||
try {
|
try {
|
||||||
|
@ -76,79 +79,28 @@ public class ApplicationManager implements XmlRpcHandler {
|
||||||
|
|
||||||
if ((appName.indexOf(".") == -1) &&
|
if ((appName.indexOf(".") == -1) &&
|
||||||
(applications.get(appName) == null)) {
|
(applications.get(appName) == null)) {
|
||||||
start(appName);
|
AppDescriptor appDesc = new AppDescriptor(appName);
|
||||||
register(appName);
|
appDesc.start();
|
||||||
|
appDesc.bind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// then stop deleted ones
|
// then stop deleted ones
|
||||||
for (Enumeration e = applications.keys(); e.hasMoreElements();) {
|
for (Enumeration e = descriptors.elements(); e.hasMoreElements();) {
|
||||||
String appName = (String) e.nextElement();
|
AppDescriptor appDesc = (AppDescriptor) e.nextElement();
|
||||||
|
|
||||||
// check if application has been removed and should be stopped
|
// check if application has been removed and should be stopped
|
||||||
if (!props.containsKey(appName)) {
|
if (!props.containsKey(appDesc.appName)) {
|
||||||
stop(appName);
|
appDesc.stop();
|
||||||
} else if (server.http != null) {
|
} else if (server.http != null) {
|
||||||
// check if application should be remounted at a
|
// If application continues to run, remount
|
||||||
// different location on embedded web server
|
// as the mounting options may have changed.
|
||||||
String oldMountpoint = mountpoints.getProperty(appName);
|
appDesc.unbind();
|
||||||
String mountpoint = getMountpoint(appName);
|
AppDescriptor ndesc = new AppDescriptor(appDesc.appName);
|
||||||
String pattern = getPathPattern(mountpoint);
|
ndesc.app = appDesc.app;
|
||||||
|
ndesc.bind();
|
||||||
|
descriptors.put(ndesc.appName, ndesc);
|
||||||
|
|
||||||
if (!pattern.equals(oldMountpoint)) {
|
|
||||||
Server.getLogger().log("Moving application " + appName +
|
|
||||||
" from " + oldMountpoint + " to " +
|
|
||||||
pattern);
|
|
||||||
|
|
||||||
HttpContext oldContext = server.http.getContext(null,
|
|
||||||
oldMountpoint);
|
|
||||||
|
|
||||||
if (oldContext != null) {
|
|
||||||
// oldContext.setContextPath(pattern);
|
|
||||||
oldContext.stop();
|
|
||||||
oldContext.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
Application app = (Application) applications.get(appName);
|
|
||||||
|
|
||||||
if (!app.hasExplicitBaseURI()) {
|
|
||||||
app.setBaseURI(mountpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
ServletHttpContext context = new ServletHttpContext();
|
|
||||||
|
|
||||||
context.setContextPath(pattern);
|
|
||||||
server.http.addContext(context);
|
|
||||||
|
|
||||||
ServletHolder holder = context.addServlet(appName, "/*",
|
|
||||||
"helma.servlet.EmbeddedServletClient");
|
|
||||||
|
|
||||||
holder.setInitParameter("application", appName);
|
|
||||||
holder.setInitParameter("mountpoint", mountpoint);
|
|
||||||
|
|
||||||
if ("true".equalsIgnoreCase(props.getProperty(appName +
|
|
||||||
".responseEncoding"))) {
|
|
||||||
context.addHandler(new ContentEncodingHandler());
|
|
||||||
}
|
|
||||||
|
|
||||||
String cookieDomain = props.getProperty(appName +
|
|
||||||
".cookieDomain");
|
|
||||||
|
|
||||||
if (cookieDomain != null) {
|
|
||||||
holder.setInitParameter("cookieDomain", cookieDomain);
|
|
||||||
}
|
|
||||||
|
|
||||||
String uploadLimit = props.getProperty(appName +
|
|
||||||
".uploadLimit");
|
|
||||||
|
|
||||||
if (uploadLimit != null) {
|
|
||||||
holder.setInitParameter("uploadLimit", uploadLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
// holder.start ();
|
|
||||||
context.start();
|
|
||||||
mountpoints.setProperty(appName, pattern);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception mx) {
|
} catch (Exception mx) {
|
||||||
|
@ -159,134 +111,38 @@ public class ApplicationManager implements XmlRpcHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void start(String appName) {
|
|
||||||
Server.getLogger().log("Building application " + appName);
|
|
||||||
|
|
||||||
try {
|
/**
|
||||||
// check if application and db dirs are set, otherwise go with
|
* Start an application by name
|
||||||
// the defaults, passing null dirs to the constructor.
|
*/
|
||||||
String appDirName = props.getProperty(appName + ".appdir");
|
public void start(String appName) {
|
||||||
File appDir = (appDirName == null) ? null : new File(appDirName);
|
AppDescriptor desc = new AppDescriptor(appName);
|
||||||
String dbDirName = props.getProperty(appName + ".dbdir");
|
desc.start();
|
||||||
File dbDir = (dbDirName == null) ? null : new File(dbDirName);
|
|
||||||
|
|
||||||
// create the application instance
|
|
||||||
Application app = new Application(appName, server, appDir, dbDir);
|
|
||||||
|
|
||||||
applications.put(appName, app);
|
|
||||||
|
|
||||||
// the application is started later in the register method, when it's bound
|
|
||||||
app.init();
|
|
||||||
} catch (Exception x) {
|
|
||||||
Server.getLogger().log("Error creating application " + appName + ": " + x);
|
|
||||||
x.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop(String appName) {
|
/**
|
||||||
Server.getLogger().log("Stopping application " + appName);
|
* Bind an application by name
|
||||||
|
*/
|
||||||
try {
|
public void register(String appName) {
|
||||||
Application app = (Application) applications.get(appName);
|
AppDescriptor desc = (AppDescriptor) descriptors.get(appName);
|
||||||
|
if (desc != null) {
|
||||||
// unbind from RMI server
|
desc.bind();
|
||||||
if (port > 0) {
|
|
||||||
Naming.unbind("//:" + port + "/" + appName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unbind from Jetty HTTP server
|
|
||||||
if (server.http != null) {
|
|
||||||
String mountpoint = mountpoints.getProperty(appName);
|
|
||||||
HttpContext context = server.http.getContext(null, mountpoint);
|
|
||||||
|
|
||||||
if (context != null) {
|
|
||||||
context.stop();
|
|
||||||
context.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unregister as XML-RPC handler
|
|
||||||
xmlrpcHandlers.remove(app.getXmlRpcHandlerName());
|
|
||||||
app.stop();
|
|
||||||
Server.getLogger().log("Unregistered application " + appName);
|
|
||||||
} catch (Exception x) {
|
|
||||||
Server.getLogger().log("Couldn't unregister app: " + x);
|
|
||||||
}
|
|
||||||
|
|
||||||
applications.remove(appName);
|
|
||||||
}
|
|
||||||
|
|
||||||
void register(String appName) {
|
|
||||||
try {
|
|
||||||
Server.getLogger().log("Binding application " + appName);
|
|
||||||
|
|
||||||
Application app = (Application) applications.get(appName);
|
|
||||||
|
|
||||||
// bind to RMI server
|
|
||||||
if (port > 0) {
|
|
||||||
Naming.rebind("//:" + port + "/" + appName, new RemoteApplication(app));
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind to Jetty HTTP server
|
|
||||||
if (server.http != null) {
|
|
||||||
String mountpoint = getMountpoint(appName);
|
|
||||||
|
|
||||||
// if using embedded webserver (not AJP) set application URL prefix
|
|
||||||
if (!app.hasExplicitBaseURI()) {
|
|
||||||
app.setBaseURI(mountpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
String pattern = getPathPattern(mountpoint);
|
|
||||||
ServletHttpContext context = new ServletHttpContext();
|
|
||||||
|
|
||||||
context.setContextPath(pattern);
|
|
||||||
server.http.addContext(context);
|
|
||||||
|
|
||||||
ServletHolder holder = context.addServlet(appName, "/*",
|
|
||||||
"helma.servlet.EmbeddedServletClient");
|
|
||||||
|
|
||||||
holder.setInitParameter("application", appName);
|
|
||||||
holder.setInitParameter("mountpoint", mountpoint);
|
|
||||||
|
|
||||||
if ("true".equalsIgnoreCase(props.getProperty(appName +
|
|
||||||
".responseEncoding"))) {
|
|
||||||
context.addHandler(new ContentEncodingHandler());
|
|
||||||
}
|
|
||||||
|
|
||||||
String cookieDomain = props.getProperty(appName + ".cookieDomain");
|
|
||||||
|
|
||||||
if (cookieDomain != null) {
|
|
||||||
holder.setInitParameter("cookieDomain", cookieDomain);
|
|
||||||
}
|
|
||||||
|
|
||||||
String uploadLimit = props.getProperty(appName + ".uploadLimit");
|
|
||||||
|
|
||||||
if (uploadLimit != null) {
|
|
||||||
holder.setInitParameter("uploadLimit", uploadLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
String debug = props.getProperty(appName + ".debug");
|
|
||||||
|
|
||||||
if (debug != null) {
|
|
||||||
holder.setInitParameter("debug", debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
// holder.start ();
|
|
||||||
context.start();
|
|
||||||
mountpoints.setProperty(appName, pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
// register as XML-RPC handler
|
|
||||||
xmlrpcHandlers.put(app.getXmlRpcHandlerName(), app);
|
|
||||||
app.start();
|
|
||||||
} catch (Exception x) {
|
|
||||||
Server.getLogger().log("Couldn't register and start app: " + x);
|
|
||||||
x.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Stop an application by name
|
||||||
|
*/
|
||||||
|
public void stop(String appName) {
|
||||||
|
AppDescriptor desc = (AppDescriptor) descriptors.get(appName);
|
||||||
|
if (desc != null) {
|
||||||
|
desc.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start all applications listed in the properties
|
||||||
*/
|
*/
|
||||||
public void startAll() {
|
public void startAll() {
|
||||||
try {
|
try {
|
||||||
|
@ -294,33 +150,14 @@ public class ApplicationManager implements XmlRpcHandler {
|
||||||
String appName = (String) e.nextElement();
|
String appName = (String) e.nextElement();
|
||||||
|
|
||||||
if (appName.indexOf(".") == -1) {
|
if (appName.indexOf(".") == -1) {
|
||||||
start(appName);
|
AppDescriptor desc = new AppDescriptor(appName);
|
||||||
|
desc.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Enumeration e = props.keys(); e.hasMoreElements();) {
|
for (Enumeration e = descriptors.elements(); e.hasMoreElements();) {
|
||||||
String appName = (String) e.nextElement();
|
AppDescriptor appDesc = (AppDescriptor) e.nextElement();
|
||||||
|
appDesc.bind();
|
||||||
if (appName.indexOf(".") == -1) {
|
|
||||||
register(appName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server.http != null) {
|
|
||||||
// add handler for static files.
|
|
||||||
File staticContent = new File(server.getHopHome(), "static");
|
|
||||||
|
|
||||||
Server.getLogger().log("Serving static content from " +
|
|
||||||
staticContent.getAbsolutePath());
|
|
||||||
|
|
||||||
HttpContext context = server.http.addContext("/static/*");
|
|
||||||
|
|
||||||
context.setResourceBase(staticContent.getAbsolutePath());
|
|
||||||
|
|
||||||
ResourceHandler handler = new ResourceHandler();
|
|
||||||
|
|
||||||
context.addHandler(handler);
|
|
||||||
context.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastModified = System.currentTimeMillis();
|
lastModified = System.currentTimeMillis();
|
||||||
|
@ -331,13 +168,13 @@ public class ApplicationManager implements XmlRpcHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Stop all running applications.
|
||||||
*/
|
*/
|
||||||
public void stopAll() {
|
public void stopAll() {
|
||||||
for (Enumeration en = applications.keys(); en.hasMoreElements();) {
|
for (Enumeration en = descriptors.elements(); en.hasMoreElements();) {
|
||||||
String appName = (String) en.nextElement();
|
AppDescriptor appDesc = (AppDescriptor) en.nextElement();
|
||||||
|
|
||||||
stop(appName);
|
appDesc.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,6 +212,12 @@ public class ApplicationManager implements XmlRpcHandler {
|
||||||
String method2 = method.substring(dot + 1);
|
String method2 = method.substring(dot + 1);
|
||||||
Application app = (Application) xmlrpcHandlers.get(handler);
|
Application app = (Application) xmlrpcHandlers.get(handler);
|
||||||
|
|
||||||
|
if (app == null) {
|
||||||
|
app = (Application) xmlrpcHandlers.get("*");
|
||||||
|
// use the original method name, the handler is resolved within the app.
|
||||||
|
method2 = method;
|
||||||
|
}
|
||||||
|
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
throw new Exception("Handler \"" + handler + "\" not found for " + method);
|
throw new Exception("Handler \"" + handler + "\" not found for " + method);
|
||||||
}
|
}
|
||||||
|
@ -382,13 +225,7 @@ public class ApplicationManager implements XmlRpcHandler {
|
||||||
return app.executeXmlRpc(method2, params);
|
return app.executeXmlRpc(method2, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMountpoint(String appName) {
|
private String getMountpoint(String mountpoint) {
|
||||||
String mountpoint = props.getProperty(appName + ".mountpoint");
|
|
||||||
|
|
||||||
if (mountpoint == null) {
|
|
||||||
return "/" + URLEncoder.encode(appName);
|
|
||||||
}
|
|
||||||
|
|
||||||
mountpoint = mountpoint.trim();
|
mountpoint = mountpoint.trim();
|
||||||
|
|
||||||
if ("".equals(mountpoint)) {
|
if ("".equals(mountpoint)) {
|
||||||
|
@ -400,15 +237,227 @@ public class ApplicationManager implements XmlRpcHandler {
|
||||||
return mountpoint;
|
return mountpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String joinMountpoint(String prefix, String suffix) {
|
||||||
|
if (prefix.endsWith("/") || suffix.startsWith("/")) {
|
||||||
|
return prefix+suffix;
|
||||||
|
} else {
|
||||||
|
return prefix+"/"+suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String getPathPattern(String mountpoint) {
|
private String getPathPattern(String mountpoint) {
|
||||||
|
if (!mountpoint.startsWith("/")) {
|
||||||
|
mountpoint = "/"+mountpoint;
|
||||||
|
}
|
||||||
|
|
||||||
if ("/".equals(mountpoint)) {
|
if ("/".equals(mountpoint)) {
|
||||||
return "/";
|
return "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mountpoint.endsWith("/")) {
|
if (mountpoint.endsWith("/")) {
|
||||||
|
return mountpoint + "*";
|
||||||
|
}
|
||||||
|
|
||||||
return mountpoint + "/*";
|
return mountpoint + "/*";
|
||||||
}
|
}
|
||||||
|
|
||||||
return mountpoint + "*";
|
/**
|
||||||
|
* Inner class that describes an application and its start settings.
|
||||||
|
*/
|
||||||
|
class AppDescriptor {
|
||||||
|
|
||||||
|
Application app;
|
||||||
|
|
||||||
|
String appName;
|
||||||
|
File appDir;
|
||||||
|
File dbDir;
|
||||||
|
String mountpoint;
|
||||||
|
String pathPattern;
|
||||||
|
String staticDir;
|
||||||
|
String staticMountpoint;
|
||||||
|
String xmlrpcHandlerName;
|
||||||
|
String cookieDomain;
|
||||||
|
String uploadLimit;
|
||||||
|
String debug;
|
||||||
|
String charset;
|
||||||
|
boolean encode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an AppDescriptor from the properties.
|
||||||
|
*/
|
||||||
|
AppDescriptor(String name) {
|
||||||
|
appName = name;
|
||||||
|
mountpoint = getMountpoint(props.getProperty(name+".mountpoint",
|
||||||
|
appName));
|
||||||
|
pathPattern = getPathPattern(mountpoint);
|
||||||
|
staticDir = props.getProperty(name+".static");
|
||||||
|
staticMountpoint = getPathPattern(props.getProperty(name+".staticMountpoint",
|
||||||
|
joinMountpoint(mountpoint, "static")));
|
||||||
|
cookieDomain = props.getProperty(name+".cookieDomain");
|
||||||
|
uploadLimit = props.getProperty(name+".uploadLimit");
|
||||||
|
debug = props.getProperty(name+".debug");
|
||||||
|
encode = "true".equalsIgnoreCase(props.getProperty(name +
|
||||||
|
".responseEncoding"));
|
||||||
|
String appDirName = props.getProperty(name + ".appdir");
|
||||||
|
appDir = (appDirName == null) ? null : new File(appDirName);
|
||||||
|
String dbDirName = props.getProperty(name + ".dbdir");
|
||||||
|
dbDir = (dbDirName == null) ? null : new File(dbDirName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void start() {
|
||||||
|
Server.getLogger().log("Building application " + appName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// create the application instance
|
||||||
|
app = new Application(appName, server, appDir, dbDir);
|
||||||
|
|
||||||
|
// register ourselves
|
||||||
|
descriptors.put(appName, this);
|
||||||
|
applications.put(appName, app);
|
||||||
|
|
||||||
|
// the application is started later in the register method, when it's bound
|
||||||
|
app.init();
|
||||||
|
app.start();
|
||||||
|
} catch (Exception x) {
|
||||||
|
Server.getLogger().log("Error creating application " + appName + ": " + x);
|
||||||
|
x.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
Server.getLogger().log("Stopping application " + appName);
|
||||||
|
|
||||||
|
// unbind application
|
||||||
|
unbind();
|
||||||
|
|
||||||
|
// stop application
|
||||||
|
try {
|
||||||
|
app.stop();
|
||||||
|
Server.getLogger().log("Stopped application " + appName);
|
||||||
|
} catch (Exception x) {
|
||||||
|
Server.getLogger().log("Couldn't stop app: " + x);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptors.remove(appName);
|
||||||
|
applications.remove(appName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind() {
|
||||||
|
try {
|
||||||
|
Server.getLogger().log("Binding application " + appName);
|
||||||
|
|
||||||
|
// bind to RMI server
|
||||||
|
if (port > 0) {
|
||||||
|
Naming.rebind("//:" + port + "/" + appName, new RemoteApplication(app));
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind to Jetty HTTP server
|
||||||
|
if (server.http != null) {
|
||||||
|
// if using embedded webserver (not AJP) set application URL prefix
|
||||||
|
if (!app.hasExplicitBaseURI()) {
|
||||||
|
app.setBaseURI(mountpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServletHttpContext context = new ServletHttpContext();
|
||||||
|
|
||||||
|
context.setContextPath(pathPattern);
|
||||||
|
server.http.addContext(context);
|
||||||
|
|
||||||
|
if (encode) {
|
||||||
|
context.addHandler(new ContentEncodingHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
ServletHolder holder = context.addServlet(appName, "/*",
|
||||||
|
"helma.servlet.EmbeddedServletClient");
|
||||||
|
|
||||||
|
holder.setInitParameter("application", appName);
|
||||||
|
// holder.setInitParameter("mountpoint", mountpoint);
|
||||||
|
|
||||||
|
if (cookieDomain != null) {
|
||||||
|
holder.setInitParameter("cookieDomain", cookieDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadLimit != null) {
|
||||||
|
holder.setInitParameter("uploadLimit", uploadLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug != null) {
|
||||||
|
holder.setInitParameter("debug", debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.setInitParameter("charset", app.getCharset());
|
||||||
|
|
||||||
|
context.start();
|
||||||
|
|
||||||
|
if (staticDir != null) {
|
||||||
|
|
||||||
|
File staticContent = new File(staticDir);
|
||||||
|
if (!staticContent.isAbsolute()) {
|
||||||
|
staticContent = new File(server.getHopHome(), staticDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
Server.getLogger().log("Serving static from " +
|
||||||
|
staticContent.getAbsolutePath());
|
||||||
|
Server.getLogger().log("Mounting static at " +
|
||||||
|
staticMountpoint);
|
||||||
|
|
||||||
|
HttpContext cx = server.http.addContext(staticMountpoint);
|
||||||
|
|
||||||
|
cx.setResourceBase(staticContent.getAbsolutePath());
|
||||||
|
|
||||||
|
ResourceHandler handler = new ResourceHandler();
|
||||||
|
|
||||||
|
cx.addHandler(handler);
|
||||||
|
cx.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register as XML-RPC handler
|
||||||
|
xmlrpcHandlerName = app.getXmlRpcHandlerName();
|
||||||
|
xmlrpcHandlers.put(xmlrpcHandlerName, app);
|
||||||
|
// app.start();
|
||||||
|
} catch (Exception x) {
|
||||||
|
Server.getLogger().log("Couldn't bind app: " + x);
|
||||||
|
x.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbind() {
|
||||||
|
Server.getLogger().log("Unbinding application " + appName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// unbind from RMI server
|
||||||
|
if (port > 0) {
|
||||||
|
Naming.unbind("//:" + port + "/" + appName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unbind from Jetty HTTP server
|
||||||
|
if (server.http != null) {
|
||||||
|
HttpContext context = server.http.getContext(null, pathPattern);
|
||||||
|
|
||||||
|
if (context != null) {
|
||||||
|
context.stop();
|
||||||
|
context.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (staticDir != null) {
|
||||||
|
context = server.http.getContext(null, staticMountpoint);
|
||||||
|
|
||||||
|
if (context != null) {
|
||||||
|
context.stop();
|
||||||
|
context.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unregister as XML-RPC handler
|
||||||
|
xmlrpcHandlers.remove(xmlrpcHandlerName);
|
||||||
|
} catch (Exception x) {
|
||||||
|
Server.getLogger().log("Couldn't unbind app: " + x);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ import java.util.*;
|
||||||
* Helma server main class.
|
* Helma server main class.
|
||||||
*/
|
*/
|
||||||
public class Server implements IPathElement, Runnable {
|
public class Server implements IPathElement, Runnable {
|
||||||
public static final String version = "1.2.4 (2003/04/16)";
|
public static final String version = "1.2.5 (2003/06/06)";
|
||||||
|
|
||||||
// server-wide properties
|
// server-wide properties
|
||||||
static SystemProperties appsProps;
|
static SystemProperties appsProps;
|
||||||
|
|
|
@ -26,10 +26,16 @@ public final class DbColumn {
|
||||||
private final int type;
|
private final int type;
|
||||||
private final Relation relation;
|
private final Relation relation;
|
||||||
|
|
||||||
|
private final boolean isId;
|
||||||
|
private final boolean isPrototype;
|
||||||
|
private final boolean isName;
|
||||||
|
|
||||||
|
private final boolean isMapped;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public DbColumn(String name, int type, Relation rel) {
|
public DbColumn(String name, int type, Relation rel, DbMapping dbmap) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.relation = rel;
|
this.relation = rel;
|
||||||
|
@ -37,6 +43,12 @@ public final class DbColumn {
|
||||||
if (relation != null) {
|
if (relation != null) {
|
||||||
relation.setColumnType(type);
|
relation.setColumnType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isId = name.equalsIgnoreCase(dbmap.getIDField());
|
||||||
|
isPrototype = name.equalsIgnoreCase(dbmap.getPrototypeField());
|
||||||
|
isName = name.equalsIgnoreCase(dbmap.getNameField());
|
||||||
|
|
||||||
|
isMapped = relation != null || isId || isPrototype || isName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,4 +71,33 @@ public final class DbColumn {
|
||||||
public Relation getRelation() {
|
public Relation getRelation() {
|
||||||
return relation;
|
return relation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this column serves as ID field for the prototype.
|
||||||
|
*/
|
||||||
|
public boolean isIdField() {
|
||||||
|
return isId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this column serves as prototype field for the prototype.
|
||||||
|
*/
|
||||||
|
public boolean isPrototypeField() {
|
||||||
|
return isPrototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this column serves as name field for the prototype.
|
||||||
|
*/
|
||||||
|
public boolean isNameField() {
|
||||||
|
return isName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this field is mapped by the prototype's db mapping.
|
||||||
|
*/
|
||||||
|
public boolean isMapped() {
|
||||||
|
return isMapped;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,9 +46,9 @@ public final class DbKey implements Key, Serializable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param what ...
|
* @param what the other key to be compared with this one
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return true if both keys are identical
|
||||||
*/
|
*/
|
||||||
public boolean equals(Object what) {
|
public boolean equals(Object what) {
|
||||||
if (what == this) {
|
if (what == this) {
|
||||||
|
@ -69,7 +69,7 @@ public final class DbKey implements Key, Serializable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return this key's hash code
|
||||||
*/
|
*/
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
if (hashcode == 0) {
|
if (hashcode == 0) {
|
||||||
|
@ -84,7 +84,7 @@ public final class DbKey implements Key, Serializable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return the key of this key's object's parent object
|
||||||
*/
|
*/
|
||||||
public Key getParentKey() {
|
public Key getParentKey() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -93,7 +93,7 @@ public final class DbKey implements Key, Serializable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return the unique storage name for this key's object
|
||||||
*/
|
*/
|
||||||
public String getStorageName() {
|
public String getStorageName() {
|
||||||
return storageName;
|
return storageName;
|
||||||
|
@ -102,7 +102,7 @@ public final class DbKey implements Key, Serializable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return this key's object's id
|
||||||
*/
|
*/
|
||||||
public String getID() {
|
public String getID() {
|
||||||
return id;
|
return id;
|
||||||
|
@ -111,7 +111,7 @@ public final class DbKey implements Key, Serializable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return a string representation for this key
|
||||||
*/
|
*/
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return (storageName == null) ? ("[" + id + "]") : (storageName + "[" + id + "]");
|
return (storageName == null) ? ("[" + id + "]") : (storageName + "[" + id + "]");
|
||||||
|
|
|
@ -17,12 +17,14 @@
|
||||||
package helma.objectmodel.db;
|
package helma.objectmodel.db;
|
||||||
|
|
||||||
import helma.framework.core.Application;
|
import helma.framework.core.Application;
|
||||||
|
import helma.framework.core.Prototype;
|
||||||
import helma.util.SystemProperties;
|
import helma.util.SystemProperties;
|
||||||
import helma.util.Updatable;
|
import helma.util.Updatable;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
@ -75,6 +77,9 @@ public final class DbMapping implements Updatable {
|
||||||
// Map of db columns by name
|
// Map of db columns by name
|
||||||
HashMap columnMap;
|
HashMap columnMap;
|
||||||
|
|
||||||
|
// Array of aggressively loaded references
|
||||||
|
Relation[] joins;
|
||||||
|
|
||||||
// pre-rendered select statement
|
// pre-rendered select statement
|
||||||
String selectString = null;
|
String selectString = null;
|
||||||
String insertString = null;
|
String insertString = null;
|
||||||
|
@ -159,6 +164,7 @@ public final class DbMapping implements Updatable {
|
||||||
return props.lastModified() != lastTypeChange;
|
return props.lastModified() != lastTypeChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the mapping from the Properties. Return true if the properties were changed.
|
* Read the mapping from the Properties. Return true if the properties were changed.
|
||||||
* The read is split in two, this method and the rewire method. The reason is that in order
|
* The read is split in two, this method and the rewire method. The reason is that in order
|
||||||
|
@ -173,9 +179,6 @@ public final class DbMapping implements Updatable {
|
||||||
// can be stored in this table
|
// can be stored in this table
|
||||||
prototypeField = props.getProperty("_prototypefield");
|
prototypeField = props.getProperty("_prototypefield");
|
||||||
|
|
||||||
// see if this prototype extends (inherits from) any other prototype
|
|
||||||
extendsProto = props.getProperty("_extends");
|
|
||||||
|
|
||||||
dbSourceName = props.getProperty("_db");
|
dbSourceName = props.getProperty("_db");
|
||||||
|
|
||||||
if (dbSourceName != null) {
|
if (dbSourceName != null) {
|
||||||
|
@ -220,19 +223,43 @@ public final class DbMapping implements Updatable {
|
||||||
|
|
||||||
lastTypeChange = props.lastModified();
|
lastTypeChange = props.lastModified();
|
||||||
|
|
||||||
|
// see if this prototype extends (inherits from) any other prototype
|
||||||
|
extendsProto = props.getProperty("_extends");
|
||||||
|
|
||||||
|
if (extendsProto != null) {
|
||||||
|
parentMapping = app.getDbMapping(extendsProto);
|
||||||
|
if (parentMapping != null && parentMapping.needsUpdate()) {
|
||||||
|
parentMapping.update();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parentMapping = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the parent prototype in the corresponding Prototype object!
|
||||||
|
// this was previously done by TypeManager, but we need to do it
|
||||||
|
// ourself because DbMapping.update() may be called by other code than
|
||||||
|
// the TypeManager.
|
||||||
|
if (typename != null &&
|
||||||
|
!"global".equalsIgnoreCase(typename) &&
|
||||||
|
!"hopobject".equalsIgnoreCase(typename)) {
|
||||||
|
Prototype proto = app.getPrototypeByName(typename);
|
||||||
|
if (proto != null) {
|
||||||
|
if (extendsProto != null) {
|
||||||
|
proto.setParentPrototype(app.getPrototypeByName(extendsProto));
|
||||||
|
} else if (!app.isJavaPrototype(typename)) {
|
||||||
|
proto.setParentPrototype(app.getPrototypeByName("hopobject"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// null the cached columns and select string
|
// null the cached columns and select string
|
||||||
columns = null;
|
columns = null;
|
||||||
columnMap.clear();
|
columnMap.clear();
|
||||||
selectString = insertString = updateString = null;
|
selectString = insertString = updateString = null;
|
||||||
|
|
||||||
if (extendsProto != null) {
|
|
||||||
parentMapping = app.getDbMapping(extendsProto);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (tableName != null && dbSource != null) {
|
|
||||||
// app.logEvent ("set data dbSource for "+typename+" to "+dbSource);
|
|
||||||
HashMap p2d = new HashMap();
|
HashMap p2d = new HashMap();
|
||||||
HashMap d2p = new HashMap();
|
HashMap d2p = new HashMap();
|
||||||
|
ArrayList joinList = new ArrayList();
|
||||||
|
|
||||||
for (Enumeration e = props.keys(); e.hasMoreElements();) {
|
for (Enumeration e = props.keys(); e.hasMoreElements();) {
|
||||||
String propName = (String) e.nextElement();
|
String propName = (String) e.nextElement();
|
||||||
|
@ -259,7 +286,22 @@ public final class DbMapping implements Updatable {
|
||||||
if ((rel.columnName != null) &&
|
if ((rel.columnName != null) &&
|
||||||
((rel.reftype == Relation.PRIMITIVE) ||
|
((rel.reftype == Relation.PRIMITIVE) ||
|
||||||
(rel.reftype == Relation.REFERENCE))) {
|
(rel.reftype == Relation.REFERENCE))) {
|
||||||
d2p.put(rel.columnName.toUpperCase(), rel);
|
Relation old = (Relation) d2p.put(rel.columnName.toUpperCase(), rel);
|
||||||
|
// check if we're overwriting another relation
|
||||||
|
// if so, primitive relations get precendence to references
|
||||||
|
if (old != null) {
|
||||||
|
app.logEvent("*** Duplicate mapping for "+typename+"."+rel.columnName);
|
||||||
|
if (old.reftype == Relation.PRIMITIVE) {
|
||||||
|
d2p.put(old.columnName.toUpperCase(), old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a reference is aggressively fetched
|
||||||
|
if ((rel.reftype == Relation.REFERENCE ||
|
||||||
|
rel.reftype == Relation.COMPLEX_REFERENCE) &&
|
||||||
|
rel.aggressiveLoading) {
|
||||||
|
joinList.add(rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// app.logEvent ("Mapping "+propName+" -> "+dbField);
|
// app.logEvent ("Mapping "+propName+" -> "+dbField);
|
||||||
|
@ -272,6 +314,9 @@ public final class DbMapping implements Updatable {
|
||||||
prop2db = p2d;
|
prop2db = p2d;
|
||||||
db2prop = d2p;
|
db2prop = d2p;
|
||||||
|
|
||||||
|
joins = new Relation[joinList.size()];
|
||||||
|
joins = (Relation[]) joinList.toArray(joins);
|
||||||
|
|
||||||
String subnodeMapping = props.getProperty("_children");
|
String subnodeMapping = props.getProperty("_children");
|
||||||
|
|
||||||
if (subnodeMapping != null) {
|
if (subnodeMapping != null) {
|
||||||
|
@ -806,20 +851,31 @@ public final class DbMapping implements Updatable {
|
||||||
|
|
||||||
// ok, we have the meta data, now loop through mapping...
|
// ok, we have the meta data, now loop through mapping...
|
||||||
int ncols = meta.getColumnCount();
|
int ncols = meta.getColumnCount();
|
||||||
|
ArrayList list = new ArrayList(ncols);
|
||||||
columns = new DbColumn[ncols];
|
|
||||||
|
|
||||||
for (int i = 0; i < ncols; i++) {
|
for (int i = 0; i < ncols; i++) {
|
||||||
String colName = meta.getColumnName(i + 1);
|
String colName = meta.getColumnName(i + 1);
|
||||||
Relation rel = columnNameToRelation(colName);
|
Relation rel = columnNameToRelation(colName);
|
||||||
|
|
||||||
columns[i] = new DbColumn(colName, meta.getColumnType(i + 1), rel);
|
DbColumn col = new DbColumn(colName, meta.getColumnType(i + 1), rel, this);
|
||||||
|
// if (col.isMapped()) {
|
||||||
|
list.add(col);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
columns = new DbColumn[list.size()];
|
||||||
|
columns = (DbColumn[]) list.toArray(columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the array of relations that are fetched with objects of this type.
|
||||||
|
*/
|
||||||
|
public Relation[] getJoins() {
|
||||||
|
return joins;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -832,6 +888,7 @@ public final class DbMapping implements Updatable {
|
||||||
*/
|
*/
|
||||||
public DbColumn getColumn(String columnName)
|
public DbColumn getColumn(String columnName)
|
||||||
throws ClassNotFoundException, SQLException {
|
throws ClassNotFoundException, SQLException {
|
||||||
|
|
||||||
DbColumn col = (DbColumn) columnMap.get(columnName);
|
DbColumn col = (DbColumn) columnMap.get(columnName);
|
||||||
|
|
||||||
if (col == null) {
|
if (col == null) {
|
||||||
|
@ -849,10 +906,6 @@ public final class DbMapping implements Updatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (col == null) {
|
|
||||||
throw new SQLException("Column " + columnName + " not found in " + this);
|
|
||||||
}
|
|
||||||
|
|
||||||
columnMap.put(columnName, col);
|
columnMap.put(columnName, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -860,12 +913,13 @@ public final class DbMapping implements Updatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get a StringBuffer initialized to the first part of the select statement
|
||||||
|
* for objects defined by this DbMapping
|
||||||
*
|
*
|
||||||
|
* @return the StringBuffer containing the first part of the select query
|
||||||
*
|
*
|
||||||
* @return ...
|
* @throws SQLException if the table meta data could not be retrieved
|
||||||
*
|
* @throws ClassNotFoundException if the JDBC driver class was not found
|
||||||
* @throws SQLException ...
|
|
||||||
* @throws ClassNotFoundException ...
|
|
||||||
*/
|
*/
|
||||||
public StringBuffer getSelect() throws SQLException, ClassNotFoundException {
|
public StringBuffer getSelect() throws SQLException, ClassNotFoundException {
|
||||||
String sel = selectString;
|
String sel = selectString;
|
||||||
|
@ -874,11 +928,43 @@ public final class DbMapping implements Updatable {
|
||||||
return new StringBuffer(sel);
|
return new StringBuffer(sel);
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuffer s = new StringBuffer("SELECT * FROM ");
|
StringBuffer s = new StringBuffer("SELECT ");
|
||||||
|
|
||||||
|
/* DbColumn[] cols = columns;
|
||||||
|
|
||||||
|
if (cols == null) {
|
||||||
|
cols = getColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cols.length; i++) {
|
||||||
|
s.append(cols[i].getName());
|
||||||
|
if (i < cols.length-1) {
|
||||||
|
s.append(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < joins.length; i++) {
|
||||||
|
} */
|
||||||
|
|
||||||
|
s.append ("*");
|
||||||
|
|
||||||
|
s.append(" FROM ");
|
||||||
|
|
||||||
s.append(getTableName());
|
s.append(getTableName());
|
||||||
s.append(" ");
|
s.append(" ");
|
||||||
|
|
||||||
|
for (int i = 0; i < joins.length; i++) {
|
||||||
|
if (!joins[i].otherType.isRelational()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
s.append("LEFT JOIN ");
|
||||||
|
s.append(joins[i].otherType.getTableName());
|
||||||
|
s.append(" AS _HLM_");
|
||||||
|
s.append(joins[i].propName);
|
||||||
|
s.append(" ON ");
|
||||||
|
joins[i].renderJoinConstraints(s);
|
||||||
|
}
|
||||||
|
|
||||||
// cache rendered string for later calls.
|
// cache rendered string for later calls.
|
||||||
selectString = s.toString();
|
selectString = s.toString();
|
||||||
|
|
||||||
|
@ -944,6 +1030,11 @@ public final class DbMapping implements Updatable {
|
||||||
try {
|
try {
|
||||||
DbColumn col = getColumn(columnName);
|
DbColumn col = getColumn(columnName);
|
||||||
|
|
||||||
|
// This is not a mapped column. In case of doubt, add quotes.
|
||||||
|
if (col == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
switch (col.getType()) {
|
switch (col.getType()) {
|
||||||
case Types.CHAR:
|
case Types.CHAR:
|
||||||
case Types.VARCHAR:
|
case Types.VARCHAR:
|
||||||
|
|
124
src/helma/objectmodel/db/MultiKey.java
Normal file
124
src/helma/objectmodel/db/MultiKey.java
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* 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.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the internal representation of a database key with multiple
|
||||||
|
* columns. It is constructed from the logical table (type) name and the
|
||||||
|
* column name/column value pairs that identify the key's object
|
||||||
|
*
|
||||||
|
* NOTE: This class doesn't fully support the Key interface - getID always
|
||||||
|
* returns null since there is no unique key (at least we don't know about it).
|
||||||
|
*/
|
||||||
|
public final class MultiKey implements Key, Serializable {
|
||||||
|
// the name of the prototype which defines the storage of this object.
|
||||||
|
// this is the name of the object's prototype, or one of its ancestors.
|
||||||
|
// If null, the object is stored in the embedded db.
|
||||||
|
private final String storageName;
|
||||||
|
|
||||||
|
// the id that defines this key's object within the above storage space
|
||||||
|
private final Map parts;
|
||||||
|
|
||||||
|
// lazily initialized hashcode
|
||||||
|
private transient int hashcode = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make a key for a persistent Object, describing its datasource and key parts.
|
||||||
|
*/
|
||||||
|
public MultiKey(DbMapping dbmap, Map parts) {
|
||||||
|
this.parts = parts;
|
||||||
|
this.storageName = (dbmap == null) ? null : dbmap.getStorageTypeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param what the other key to be compared with this one
|
||||||
|
*
|
||||||
|
* @return true if both keys are identical
|
||||||
|
*/
|
||||||
|
public boolean equals(Object what) {
|
||||||
|
if (what == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(what instanceof MultiKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiKey k = (MultiKey) what;
|
||||||
|
|
||||||
|
// storageName is an interned string (by DbMapping, from where we got it)
|
||||||
|
// so we can compare by using == instead of the equals method.
|
||||||
|
return (storageName == k.storageName) &&
|
||||||
|
((parts == k.parts) || parts.equals(k.parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return this key's hash code
|
||||||
|
*/
|
||||||
|
public int hashCode() {
|
||||||
|
if (hashcode == 0) {
|
||||||
|
hashcode = (storageName == null) ? (17 + (37 * parts.hashCode()))
|
||||||
|
: (17 + (37 * storageName.hashCode()) +
|
||||||
|
(+37 * parts.hashCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return the key of this key's object's parent object
|
||||||
|
*/
|
||||||
|
public Key getParentKey() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return the unique storage name for this key's object
|
||||||
|
*/
|
||||||
|
public String getStorageName() {
|
||||||
|
return storageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return this key's object's id
|
||||||
|
*/
|
||||||
|
public String getID() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return a string representation for this key
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return (storageName == null) ? ("[" + parts + "]") : (storageName + "[" + parts + "]");
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,7 +76,7 @@ public final class Node implements INode, Serializable {
|
||||||
transient private int state;
|
transient private int state;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor is only used for instances of the NullNode subclass. Do not use for ordinary Nodes.
|
* This constructor is only used for NullNode instance. Do not use for ordinary Nodes.
|
||||||
*/
|
*/
|
||||||
Node() {
|
Node() {
|
||||||
created = lastmodified = System.currentTimeMillis();
|
created = lastmodified = System.currentTimeMillis();
|
||||||
|
@ -145,183 +145,28 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor used for nodes being stored in a relational database table.
|
* Initializer used for nodes being stored in a relational database table.
|
||||||
*/
|
*/
|
||||||
public Node(DbMapping dbm, ResultSet rs, DbColumn[] columns, WrappedNodeManager nmgr)
|
public void init(DbMapping dbm, String id, String name, String protoName,
|
||||||
throws SQLException, IOException {
|
Hashtable propMap, WrappedNodeManager nmgr) {
|
||||||
this.nmgr = nmgr;
|
this.nmgr = nmgr;
|
||||||
|
|
||||||
// see what prototype/DbMapping this object should use
|
// see what prototype/DbMapping this object should use
|
||||||
dbmap = dbm;
|
this.dbmap = dbm;
|
||||||
|
// set the prototype name
|
||||||
|
this.prototype = protoName;
|
||||||
|
|
||||||
String protoField = dbmap.getPrototypeField();
|
this.id = id;
|
||||||
|
|
||||||
if (protoField != null) {
|
|
||||||
String protoName = rs.getString(protoField);
|
|
||||||
|
|
||||||
if (protoName != null) {
|
|
||||||
dbmap = nmgr.getDbMapping(protoName);
|
|
||||||
|
|
||||||
if (dbmap == null) {
|
|
||||||
// invalid prototype name!
|
|
||||||
System.err.println("Warning: Invalid prototype name: " + protoName +
|
|
||||||
" - using default");
|
|
||||||
dbmap = dbm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setPrototype(dbmap.getTypeName());
|
|
||||||
|
|
||||||
id = rs.getString(dbmap.getIDField());
|
|
||||||
|
|
||||||
// checkWriteLock ();
|
|
||||||
String nameField = dbmap.getNameField();
|
|
||||||
|
|
||||||
name = (nameField == null) ? id : rs.getString(nameField);
|
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
// If name was not set from resultset, create a synthetical name now.
|
||||||
if ((name == null) || (name.length() == 0)) {
|
if ((name == null) || (name.length() == 0)) {
|
||||||
name = dbmap.getTypeName() + " " + id;
|
this.name = dbmap.getTypeName() + " " + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
created = lastmodified = System.currentTimeMillis();
|
this.propMap = propMap;
|
||||||
|
|
||||||
for (int i = 0; i < columns.length; i++) {
|
// set lastmodified and created timestamps and mark as clean
|
||||||
Relation rel = columns[i].getRelation();
|
|
||||||
|
|
||||||
if ((rel == null) ||
|
|
||||||
((rel.reftype != Relation.PRIMITIVE) &&
|
|
||||||
(rel.reftype != Relation.REFERENCE))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Property newprop = new Property(rel.propName, this);
|
|
||||||
|
|
||||||
switch (columns[i].getType()) {
|
|
||||||
case Types.BIT:
|
|
||||||
newprop.setBooleanValue(rs.getBoolean(columns[i].getName()));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.TINYINT:
|
|
||||||
case Types.BIGINT:
|
|
||||||
case Types.SMALLINT:
|
|
||||||
case Types.INTEGER:
|
|
||||||
newprop.setIntegerValue(rs.getLong(columns[i].getName()));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.REAL:
|
|
||||||
case Types.FLOAT:
|
|
||||||
case Types.DOUBLE:
|
|
||||||
newprop.setFloatValue(rs.getDouble(columns[i].getName()));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.DECIMAL:
|
|
||||||
case Types.NUMERIC:
|
|
||||||
|
|
||||||
BigDecimal num = rs.getBigDecimal(columns[i].getName());
|
|
||||||
|
|
||||||
if (num == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num.scale() > 0) {
|
|
||||||
newprop.setFloatValue(num.doubleValue());
|
|
||||||
} else {
|
|
||||||
newprop.setIntegerValue(num.longValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.VARBINARY:
|
|
||||||
case Types.BINARY:
|
|
||||||
newprop.setStringValue(rs.getString(columns[i].getName()));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.LONGVARBINARY:
|
|
||||||
case Types.LONGVARCHAR:
|
|
||||||
|
|
||||||
try {
|
|
||||||
newprop.setStringValue(rs.getString(columns[i].getName()));
|
|
||||||
} catch (SQLException x) {
|
|
||||||
Reader in = rs.getCharacterStream(columns[i].getName());
|
|
||||||
char[] buffer = new char[2048];
|
|
||||||
int read = 0;
|
|
||||||
int r = 0;
|
|
||||||
|
|
||||||
while ((r = in.read(buffer, read, buffer.length - read)) > -1) {
|
|
||||||
read += r;
|
|
||||||
|
|
||||||
if (read == buffer.length) {
|
|
||||||
// grow input buffer
|
|
||||||
char[] newBuffer = new char[buffer.length * 2];
|
|
||||||
|
|
||||||
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
|
|
||||||
buffer = newBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newprop.setStringValue(new String(buffer, 0, read));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.CHAR:
|
|
||||||
case Types.VARCHAR:
|
|
||||||
case Types.OTHER:
|
|
||||||
newprop.setStringValue(rs.getString(columns[i].getName()));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.DATE:
|
|
||||||
case Types.TIME:
|
|
||||||
case Types.TIMESTAMP:
|
|
||||||
newprop.setDateValue(rs.getTimestamp(columns[i].getName()));
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Types.NULL:
|
|
||||||
newprop.setStringValue(null);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
// continue;
|
|
||||||
default:
|
|
||||||
newprop.setStringValue(rs.getString(columns[i].getName()));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rs.wasNull()) {
|
|
||||||
newprop.setStringValue(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propMap == null) {
|
|
||||||
propMap = new Hashtable();
|
|
||||||
}
|
|
||||||
|
|
||||||
propMap.put(rel.propName.toLowerCase(), newprop);
|
|
||||||
|
|
||||||
// if the property is a pointer to another node, change the property type to NODE
|
|
||||||
if ((rel.reftype == Relation.REFERENCE) && rel.usesPrimaryKey()) {
|
|
||||||
// FIXME: References to anything other than the primary key are not supported
|
|
||||||
newprop.convertToNodeReference(rel.otherType);
|
|
||||||
|
|
||||||
// newprop.nhandle = new NodeHandle (new DbKey (rel.otherType, newprop.getStringValue ()));
|
|
||||||
// newprop.type = IProperty.NODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark property as clean, since it's fresh from the db
|
|
||||||
newprop.dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// again set created and lastmodified. This is because
|
|
||||||
// lastmodified has been been updated, and we want both values to
|
|
||||||
// be identical to show that the node hasn't been changed since
|
|
||||||
// it was first created.
|
|
||||||
created = lastmodified = System.currentTimeMillis();
|
created = lastmodified = System.currentTimeMillis();
|
||||||
markAs(CLEAN);
|
markAs(CLEAN);
|
||||||
}
|
}
|
||||||
|
@ -914,7 +759,7 @@ public final class Node implements INode, Serializable {
|
||||||
|
|
||||||
if (pn != null) {
|
if (pn != null) {
|
||||||
setParent((Node) pn);
|
setParent((Node) pn);
|
||||||
anonymous = !pinfo.named;
|
// anonymous = !pinfo.named;
|
||||||
lastParentSet = System.currentTimeMillis();
|
lastParentSet = System.currentTimeMillis();
|
||||||
|
|
||||||
return pn;
|
return pn;
|
||||||
|
@ -1577,7 +1422,7 @@ public final class Node implements INode, Serializable {
|
||||||
|
|
||||||
// do not fetch subnodes for nodes that haven't been persisted yet or are in
|
// do not fetch subnodes for nodes that haven't been persisted yet or are in
|
||||||
// the process of being persistified - except if "manual" subnoderelation is set.
|
// the process of being persistified - except if "manual" subnoderelation is set.
|
||||||
if (subRel.aggressiveLoading &&
|
if (subRel.aggressiveLoading && subRel.getGroup() == null &&
|
||||||
(((state != TRANSIENT) && (state != NEW)) ||
|
(((state != TRANSIENT) && (state != NEW)) ||
|
||||||
(subnodeRelation != null))) {
|
(subnodeRelation != null))) {
|
||||||
// we don't want to load *all* nodes if we just want to count them
|
// we don't want to load *all* nodes if we just want to count them
|
||||||
|
@ -1813,23 +1658,28 @@ public final class Node implements INode, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// so if we have a property relation and it does in fact link to another object...
|
// so if we have a property relation and it does in fact link to another object...
|
||||||
if ((propRel != null) && propRel.isCollection()) {
|
if ((propRel != null) && (propRel.isCollection() || propRel.isComplexReference())) {
|
||||||
// in some cases we just want to create and set a generic node without consulting
|
// in some cases we just want to create and set a generic node without consulting
|
||||||
// the NodeManager if it exists: When we get a collection (aka virtual node)
|
// the NodeManager if it exists: When we get a collection (aka virtual node)
|
||||||
// from a transient node for the first time, or when we get a collection whose
|
// from a transient node for the first time, or when we get a collection whose
|
||||||
// content objects are stored in the embedded XML data storage.
|
// content objects are stored in the embedded XML data storage.
|
||||||
if ((state == TRANSIENT) && propRel.virtual) {
|
if ((state == TRANSIENT) && propRel.virtual) {
|
||||||
INode node = new Node(propname, propRel.getPrototype(), nmgr);
|
Node pn = new Node(propname, propRel.getPrototype(), nmgr);
|
||||||
|
|
||||||
node.setDbMapping(propRel.getVirtualMapping());
|
pn.setDbMapping(propRel.getVirtualMapping());
|
||||||
setNode(propname, node);
|
pn.setParent(this);
|
||||||
|
if (propRel.needsPersistence()) {
|
||||||
|
setNode(propname, pn);
|
||||||
prop = (Property) propMap.get(propname);
|
prop = (Property) propMap.get(propname);
|
||||||
|
} else {
|
||||||
|
prop = new Property(propname, this, pn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if this is from relational database only fetch if this node
|
// if this is from relational database only fetch if this node
|
||||||
// is itself persistent.
|
// is itself persistent.
|
||||||
else if ((state != TRANSIENT) && propRel.createPropertyOnDemand()) {
|
else if ((state != TRANSIENT) && propRel.createOnDemand()) {
|
||||||
// this may be a relational node stored by property name
|
// this may be a relational node stored by property name
|
||||||
try {
|
// try {
|
||||||
Node pn = nmgr.getNode(this, propname, propRel);
|
Node pn = nmgr.getNode(this, propname, propRel);
|
||||||
|
|
||||||
if (pn != null) {
|
if (pn != null) {
|
||||||
|
@ -1842,9 +1692,9 @@ public final class Node implements INode, Serializable {
|
||||||
|
|
||||||
prop = new Property(propname, this, pn);
|
prop = new Property(propname, this, pn);
|
||||||
}
|
}
|
||||||
} catch (RuntimeException nonode) {
|
// } catch (RuntimeException nonode) {
|
||||||
// wasn't a node after all
|
// wasn't a node after all
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1985,6 +1835,41 @@ public final class Node implements INode, Serializable {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directly set a property on this node
|
||||||
|
*
|
||||||
|
* @param propname ...
|
||||||
|
* @param value ...
|
||||||
|
*/
|
||||||
|
protected void set(String propname, Object value, int type) {
|
||||||
|
checkWriteLock();
|
||||||
|
|
||||||
|
if (propMap == null) {
|
||||||
|
propMap = new Hashtable();
|
||||||
|
}
|
||||||
|
|
||||||
|
propname = propname.trim();
|
||||||
|
|
||||||
|
String p2 = propname.toLowerCase();
|
||||||
|
|
||||||
|
Property prop = (Property) propMap.get(p2);
|
||||||
|
|
||||||
|
if (prop != null) {
|
||||||
|
prop.setValue(value, type);
|
||||||
|
} else {
|
||||||
|
prop = new Property(propname, this);
|
||||||
|
prop.setValue(value, type);
|
||||||
|
propMap.put(p2, prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED));
|
||||||
|
lastmodified = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (state == CLEAN) {
|
||||||
|
markAs(MODIFIED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -2324,6 +2209,17 @@ public final class Node implements INode, Serializable {
|
||||||
|
|
||||||
String p2 = propname.toLowerCase();
|
String p2 = propname.toLowerCase();
|
||||||
|
|
||||||
|
Relation rel = (dbmap == null) ? null : dbmap.getPropertyRelation(propname);
|
||||||
|
|
||||||
|
if (rel != null && (rel.countConstraints() > 1 || rel.isComplexReference())) {
|
||||||
|
rel.setConstraints(this, n);
|
||||||
|
if (rel.isComplexReference()) {
|
||||||
|
Key key = new MultiKey(n.getDbMapping(), rel.getKeyParts(this));
|
||||||
|
nmgr.nmgr.registerNode(n, key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Property prop = (propMap == null) ? null : (Property) propMap.get(p2);
|
Property prop = (propMap == null) ? null : (Property) propMap.get(p2);
|
||||||
|
|
||||||
if (prop != null) {
|
if (prop != null) {
|
||||||
|
@ -2346,8 +2242,6 @@ public final class Node implements INode, Serializable {
|
||||||
|
|
||||||
prop.setNodeValue(n);
|
prop.setNodeValue(n);
|
||||||
|
|
||||||
Relation rel = (dbmap == null) ? null : dbmap.getPropertyRelation(propname);
|
|
||||||
|
|
||||||
if ((rel == null) || (rel.reftype == Relation.REFERENCE) || rel.virtual ||
|
if ((rel == null) || (rel.reftype == Relation.REFERENCE) || rel.virtual ||
|
||||||
(rel.otherType == null) || !rel.otherType.isRelational()) {
|
(rel.otherType == null) || !rel.otherType.isRelational()) {
|
||||||
// the node must be stored as explicit property
|
// the node must be stored as explicit property
|
||||||
|
@ -2435,6 +2329,15 @@ public final class Node implements INode, Serializable {
|
||||||
if (state == CLEAN) {
|
if (state == CLEAN) {
|
||||||
markAs(MODIFIED);
|
markAs(MODIFIED);
|
||||||
}
|
}
|
||||||
|
} else if (dbmap != null) {
|
||||||
|
// check if this is a complex constraint and we have to
|
||||||
|
// unset constraints.
|
||||||
|
Relation rel = dbmap.getExactPropertyRelation(propname);
|
||||||
|
|
||||||
|
if (rel != null && (rel.isComplexReference())) {
|
||||||
|
p = getProperty(propname);
|
||||||
|
rel.unsetConstraints(this, p.getNodeValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
}
|
}
|
||||||
|
@ -2538,8 +2441,8 @@ public final class Node implements INode, Serializable {
|
||||||
* This method walks down node path to the first non-virtual node and return it.
|
* This method walks down node path to the first non-virtual node and return it.
|
||||||
* limit max depth to 3, since there shouldn't be more then 2 layers of virtual nodes.
|
* limit max depth to 3, since there shouldn't be more then 2 layers of virtual nodes.
|
||||||
*/
|
*/
|
||||||
public INode getNonVirtualParent() {
|
public Node getNonVirtualParent() {
|
||||||
INode node = this;
|
Node node = this;
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
|
@ -2550,7 +2453,7 @@ public final class Node implements INode, Serializable {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node.getParent();
|
node = (Node) node.getParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -19,6 +19,7 @@ package helma.objectmodel.db;
|
||||||
import helma.framework.core.Application;
|
import helma.framework.core.Application;
|
||||||
import helma.objectmodel.*;
|
import helma.objectmodel.*;
|
||||||
import helma.util.CacheMap;
|
import helma.util.CacheMap;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -270,7 +271,10 @@ public final class NodeManager {
|
||||||
Key key = null;
|
Key key = null;
|
||||||
|
|
||||||
// check what kind of object we're looking for and make an apropriate key
|
// check what kind of object we're looking for and make an apropriate key
|
||||||
if (rel.virtual || (rel.groupby != null) || !rel.usesPrimaryKey()) {
|
if (rel.isComplexReference()) {
|
||||||
|
// a key for a complex reference
|
||||||
|
key = new MultiKey(rel.otherType, rel.getKeyParts(home));
|
||||||
|
} else if (rel.virtual || (rel.groupby != null) || !rel.usesPrimaryKey()) {
|
||||||
// a key for a virtually defined object that's never actually stored in the db
|
// a key for a virtually defined object that's never actually stored in the db
|
||||||
// or a key for an object that represents subobjects grouped by some property, generated on the fly
|
// or a key for an object that represents subobjects grouped by some property, generated on the fly
|
||||||
key = new SyntheticKey(home.getKey(), kstr);
|
key = new SyntheticKey(home.getKey(), kstr);
|
||||||
|
@ -395,6 +399,14 @@ public final class NodeManager {
|
||||||
cache.put(node.getKey(), node);
|
cache.put(node.getKey(), node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a node in the node cache using the key argument.
|
||||||
|
*/
|
||||||
|
protected void registerNode(Node node, Key key) {
|
||||||
|
cache.put(key, node);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a node from the node cache. If at a later time it is accessed again,
|
* Remove a node from the node cache. If at a later time it is accessed again,
|
||||||
* it will be refetched from the database.
|
* it will be refetched from the database.
|
||||||
|
@ -515,6 +527,7 @@ public final class NodeManager {
|
||||||
try {
|
try {
|
||||||
int stmtNumber = 1;
|
int stmtNumber = 1;
|
||||||
|
|
||||||
|
// first column of insert statement is always the primary key
|
||||||
stmt.setString(stmtNumber, node.getID());
|
stmt.setString(stmtNumber, node.getID());
|
||||||
|
|
||||||
Hashtable propMap = node.getPropMap();
|
Hashtable propMap = node.getPropMap();
|
||||||
|
@ -523,7 +536,7 @@ public final class NodeManager {
|
||||||
Relation rel = columns[i].getRelation();
|
Relation rel = columns[i].getRelation();
|
||||||
Property p = null;
|
Property p = null;
|
||||||
|
|
||||||
if ((rel != null) && (rel.isPrimitive() || rel.isReference())) {
|
if (rel != null && propMap != null && (rel.isPrimitive() || rel.isReference())) {
|
||||||
p = (Property) propMap.get(rel.getPropName());
|
p = (Property) propMap.get(rel.getPropName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -961,6 +974,7 @@ public final class NodeManager {
|
||||||
public List getNodeIDs(Node home, Relation rel) throws Exception {
|
public List getNodeIDs(Node home, Relation rel) throws Exception {
|
||||||
// Transactor tx = (Transactor) Thread.currentThread ();
|
// Transactor tx = (Transactor) Thread.currentThread ();
|
||||||
// tx.timer.beginEvent ("getNodeIDs "+home);
|
// tx.timer.beginEvent ("getNodeIDs "+home);
|
||||||
|
|
||||||
if ((rel == null) || (rel.otherType == null) || !rel.otherType.isRelational()) {
|
if ((rel == null) || (rel.otherType == null) || !rel.otherType.isRelational()) {
|
||||||
// this should never be called for embedded nodes
|
// this should never be called for embedded nodes
|
||||||
throw new RuntimeException("NodeMgr.getNodeIDs called for non-relational node " +
|
throw new RuntimeException("NodeMgr.getNodeIDs called for non-relational node " +
|
||||||
|
@ -982,7 +996,8 @@ public final class NodeManager {
|
||||||
|
|
||||||
if (home.getSubnodeRelation() != null) {
|
if (home.getSubnodeRelation() != null) {
|
||||||
// subnode relation was explicitly set
|
// subnode relation was explicitly set
|
||||||
q = new StringBuffer("SELECT ").append(idfield).append(" FROM ")
|
q = new StringBuffer("SELECT ").append(table).append('.')
|
||||||
|
.append(idfield).append(" FROM ")
|
||||||
.append(table).append(" ")
|
.append(table).append(" ")
|
||||||
.append(home.getSubnodeRelation())
|
.append(home.getSubnodeRelation())
|
||||||
.toString();
|
.toString();
|
||||||
|
@ -1075,6 +1090,7 @@ public final class NodeManager {
|
||||||
Connection con = dbm.getConnection();
|
Connection con = dbm.getConnection();
|
||||||
Statement stmt = con.createStatement();
|
Statement stmt = con.createStatement();
|
||||||
DbColumn[] columns = dbm.getColumns();
|
DbColumn[] columns = dbm.getColumns();
|
||||||
|
Relation[] joins = dbm.getJoins();
|
||||||
StringBuffer q = dbm.getSelect();
|
StringBuffer q = dbm.getSelect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1102,7 +1118,10 @@ public final class NodeManager {
|
||||||
|
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
// create new Nodes.
|
// create new Nodes.
|
||||||
Node node = new Node(rel.otherType, rs, columns, safe);
|
Node node = createNode(rel.otherType, rs, columns, 0);
|
||||||
|
if (node == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Key primKey = node.getKey();
|
Key primKey = node.getKey();
|
||||||
|
|
||||||
retval.add(new NodeHandle(primKey));
|
retval.add(new NodeHandle(primKey));
|
||||||
|
@ -1115,7 +1134,10 @@ public final class NodeManager {
|
||||||
cache.put(primKey, oldnode);
|
cache.put(primKey, oldnode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchJoinedNodes(rs, joins, columns.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
// tx.timer.endEvent ("getNodes "+home);
|
// tx.timer.endEvent ("getNodes "+home);
|
||||||
if (stmt != null) {
|
if (stmt != null) {
|
||||||
|
@ -1147,6 +1169,7 @@ public final class NodeManager {
|
||||||
Connection con = dbm.getConnection();
|
Connection con = dbm.getConnection();
|
||||||
Statement stmt = con.createStatement();
|
Statement stmt = con.createStatement();
|
||||||
DbColumn[] columns = dbm.getColumns();
|
DbColumn[] columns = dbm.getColumns();
|
||||||
|
Relation[] joins = dbm.getJoins();
|
||||||
StringBuffer q = dbm.getSelect();
|
StringBuffer q = dbm.getSelect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1154,6 +1177,8 @@ public final class NodeManager {
|
||||||
boolean needsQuotes = dbm.needsQuotes(idfield);
|
boolean needsQuotes = dbm.needsQuotes(idfield);
|
||||||
|
|
||||||
q.append("WHERE ");
|
q.append("WHERE ");
|
||||||
|
q.append(dbm.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(idfield);
|
q.append(idfield);
|
||||||
q.append(" IN (");
|
q.append(" IN (");
|
||||||
|
|
||||||
|
@ -1212,7 +1237,10 @@ public final class NodeManager {
|
||||||
|
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
// create new Nodes.
|
// create new Nodes.
|
||||||
Node node = new Node(dbm, rs, columns, safe);
|
Node node = createNode(dbm, rs, columns, 0);
|
||||||
|
if (node == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Key primKey = node.getKey();
|
Key primKey = node.getKey();
|
||||||
|
|
||||||
// for grouped nodes, collect subnode lists for the intermediary
|
// for grouped nodes, collect subnode lists for the intermediary
|
||||||
|
@ -1260,6 +1288,8 @@ public final class NodeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchJoinedNodes(rs, joins, columns.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If these are grouped nodes, build the intermediary group nodes
|
// If these are grouped nodes, build the intermediary group nodes
|
||||||
|
@ -1280,6 +1310,8 @@ public final class NodeManager {
|
||||||
groupnode.lastSubnodeFetch = System.currentTimeMillis();
|
groupnode.lastSubnodeFetch = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Exception x) {
|
||||||
|
System.err.println ("ERROR IN PREFETCHNODES: "+x);
|
||||||
} finally {
|
} finally {
|
||||||
if (stmt != null) {
|
if (stmt != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -1440,9 +1472,12 @@ public final class NodeManager {
|
||||||
stmt = con.createStatement();
|
stmt = con.createStatement();
|
||||||
|
|
||||||
DbColumn[] columns = dbm.getColumns();
|
DbColumn[] columns = dbm.getColumns();
|
||||||
|
Relation[] joins = dbm.getJoins();
|
||||||
StringBuffer q = dbm.getSelect();
|
StringBuffer q = dbm.getSelect();
|
||||||
|
|
||||||
q.append("WHERE ");
|
q.append("WHERE ");
|
||||||
|
q.append(dbm.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(idfield);
|
q.append(idfield);
|
||||||
q.append(" = ");
|
q.append(" = ");
|
||||||
|
|
||||||
|
@ -1464,7 +1499,9 @@ public final class NodeManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = new Node(dbm, rs, columns, safe);
|
node = createNode(dbm, rs, columns, 0);
|
||||||
|
|
||||||
|
fetchJoinedNodes(rs, joins, columns.length);
|
||||||
|
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
throw new RuntimeException("More than one value returned by query.");
|
throw new RuntimeException("More than one value returned by query.");
|
||||||
|
@ -1521,17 +1558,21 @@ public final class NodeManager {
|
||||||
|
|
||||||
Connection con = dbm.getConnection();
|
Connection con = dbm.getConnection();
|
||||||
DbColumn[] columns = dbm.getColumns();
|
DbColumn[] columns = dbm.getColumns();
|
||||||
|
Relation[] joins = dbm.getJoins();
|
||||||
StringBuffer q = dbm.getSelect();
|
StringBuffer q = dbm.getSelect();
|
||||||
|
|
||||||
if (home.getSubnodeRelation() != null) {
|
if (home.getSubnodeRelation() != null && !rel.isComplexReference()) {
|
||||||
// combine our key with the constraints in the manually set subnode relation
|
// combine our key with the constraints in the manually set subnode relation
|
||||||
q.append("WHERE ");
|
q.append("WHERE ");
|
||||||
|
q.append(dbm.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(rel.accessName);
|
q.append(rel.accessName);
|
||||||
q.append(" = '");
|
q.append(" = '");
|
||||||
q.append(escape(kstr));
|
q.append(escape(kstr));
|
||||||
q.append("'");
|
q.append("'");
|
||||||
q.append(" AND ");
|
q.append(" AND (");
|
||||||
q.append(home.getSubnodeRelation().trim().substring(5));
|
q.append(home.getSubnodeRelation().trim().substring(5));
|
||||||
|
q.append(")");
|
||||||
} else {
|
} else {
|
||||||
q.append(rel.buildQuery(home, home.getNonVirtualParent(), kstr,
|
q.append(rel.buildQuery(home, home.getNonVirtualParent(), kstr,
|
||||||
"WHERE ", false));
|
"WHERE ", false));
|
||||||
|
@ -1549,7 +1590,9 @@ public final class NodeManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = new Node(rel.otherType, rs, columns, safe);
|
node = createNode(rel.otherType, rs, columns, 0);
|
||||||
|
|
||||||
|
fetchJoinedNodes(rs, joins, columns.length);
|
||||||
|
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
throw new RuntimeException("More than one value returned by query.");
|
throw new RuntimeException("More than one value returned by query.");
|
||||||
|
@ -1564,6 +1607,7 @@ public final class NodeManager {
|
||||||
node = existing;
|
node = existing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
if (stmt != null) {
|
if (stmt != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -1577,6 +1621,221 @@ public final class NodeManager {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Node from a ResultSet.
|
||||||
|
*/
|
||||||
|
public Node createNode(DbMapping dbm, ResultSet rs, DbColumn[] columns, int offset)
|
||||||
|
throws SQLException, IOException {
|
||||||
|
Hashtable propMap = new Hashtable();
|
||||||
|
String id = null;
|
||||||
|
String name = null;
|
||||||
|
String protoName = dbm.getTypeName();
|
||||||
|
DbMapping dbmap = dbm;
|
||||||
|
|
||||||
|
Node node = new Node();
|
||||||
|
|
||||||
|
for (int i = 0; i < columns.length; i++) {
|
||||||
|
|
||||||
|
// set prototype?
|
||||||
|
if (columns[i].isPrototypeField()) {
|
||||||
|
protoName = rs.getString(i+1+offset);
|
||||||
|
|
||||||
|
if (protoName != null) {
|
||||||
|
dbmap = getDbMapping(protoName);
|
||||||
|
|
||||||
|
if (dbmap == null) {
|
||||||
|
// invalid prototype name!
|
||||||
|
System.err.println("Warning: Invalid prototype name: " + protoName +
|
||||||
|
" - using default");
|
||||||
|
dbmap = dbm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set id?
|
||||||
|
if (columns[i].isIdField()) {
|
||||||
|
id = rs.getString(i+1+offset);
|
||||||
|
// if id == null, the object doesn't actually exist - return null
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set name?
|
||||||
|
if (columns[i].isNameField()) {
|
||||||
|
name = rs.getString(i+1+offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Relation rel = columns[i].getRelation();
|
||||||
|
|
||||||
|
if ((rel == null) ||
|
||||||
|
((rel.reftype != Relation.PRIMITIVE) &&
|
||||||
|
(rel.reftype != Relation.REFERENCE))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Property newprop = new Property(rel.propName, node);
|
||||||
|
|
||||||
|
switch (columns[i].getType()) {
|
||||||
|
case Types.BIT:
|
||||||
|
newprop.setBooleanValue(rs.getBoolean(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.TINYINT:
|
||||||
|
case Types.BIGINT:
|
||||||
|
case Types.SMALLINT:
|
||||||
|
case Types.INTEGER:
|
||||||
|
newprop.setIntegerValue(rs.getLong(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.REAL:
|
||||||
|
case Types.FLOAT:
|
||||||
|
case Types.DOUBLE:
|
||||||
|
newprop.setFloatValue(rs.getDouble(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.DECIMAL:
|
||||||
|
case Types.NUMERIC:
|
||||||
|
|
||||||
|
BigDecimal num = rs.getBigDecimal(i+1+offset);
|
||||||
|
|
||||||
|
if (num == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num.scale() > 0) {
|
||||||
|
newprop.setFloatValue(num.doubleValue());
|
||||||
|
} else {
|
||||||
|
newprop.setIntegerValue(num.longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.VARBINARY:
|
||||||
|
case Types.BINARY:
|
||||||
|
newprop.setStringValue(rs.getString(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.LONGVARBINARY:
|
||||||
|
case Types.LONGVARCHAR:
|
||||||
|
|
||||||
|
try {
|
||||||
|
newprop.setStringValue(rs.getString(i+1+offset));
|
||||||
|
} catch (SQLException x) {
|
||||||
|
Reader in = rs.getCharacterStream(i+1+offset);
|
||||||
|
char[] buffer = new char[2048];
|
||||||
|
int read = 0;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
while ((r = in.read(buffer, read, buffer.length - read)) > -1) {
|
||||||
|
read += r;
|
||||||
|
|
||||||
|
if (read == buffer.length) {
|
||||||
|
// grow input buffer
|
||||||
|
char[] newBuffer = new char[buffer.length * 2];
|
||||||
|
|
||||||
|
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
|
||||||
|
buffer = newBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newprop.setStringValue(new String(buffer, 0, read));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.CHAR:
|
||||||
|
case Types.VARCHAR:
|
||||||
|
case Types.OTHER:
|
||||||
|
newprop.setStringValue(rs.getString(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.DATE:
|
||||||
|
newprop.setDateValue(rs.getDate(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.TIME:
|
||||||
|
newprop.setDateValue(rs.getTime(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.TIMESTAMP:
|
||||||
|
newprop.setDateValue(rs.getTimestamp(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Types.NULL:
|
||||||
|
newprop.setStringValue(null);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// continue;
|
||||||
|
default:
|
||||||
|
newprop.setStringValue(rs.getString(i+1+offset));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rs.wasNull()) {
|
||||||
|
newprop.setStringValue(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
propMap.put(rel.propName.toLowerCase(), newprop);
|
||||||
|
|
||||||
|
// if the property is a pointer to another node, change the property type to NODE
|
||||||
|
if ((rel.reftype == Relation.REFERENCE) && rel.usesPrimaryKey()) {
|
||||||
|
// FIXME: References to anything other than the primary key are not supported
|
||||||
|
newprop.convertToNodeReference(rel.otherType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark property as clean, since it's fresh from the db
|
||||||
|
newprop.dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.init(dbmap, id, name, protoName, propMap, safe);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch nodes that are fetched additionally to another node via join.
|
||||||
|
*/
|
||||||
|
private void fetchJoinedNodes(ResultSet rs, Relation[] joins, int offset)
|
||||||
|
throws ClassNotFoundException, SQLException, IOException {
|
||||||
|
int resultSetOffset = offset;
|
||||||
|
// create joined objects
|
||||||
|
for (int i = 0; i < joins.length; i++) {
|
||||||
|
DbMapping jdbm = joins[i].otherType;
|
||||||
|
Node node = createNode(jdbm, rs, jdbm.getColumns(), resultSetOffset);
|
||||||
|
if (node != null) {
|
||||||
|
Key primKey = node.getKey();
|
||||||
|
// register new nodes with the cache. If an up-to-date copy
|
||||||
|
// existed in the cache, use that.
|
||||||
|
synchronized (cache) {
|
||||||
|
Node oldnode = (Node) cache.put(primKey, node);
|
||||||
|
|
||||||
|
if ((oldnode != null) &&
|
||||||
|
(oldnode.getState() != INode.INVALID)) {
|
||||||
|
// found an ok version in the cache, use it.
|
||||||
|
cache.put(primKey, oldnode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultSetOffset += jdbm.getColumns().length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a DbMapping for a given prototype name. This is just a proxy
|
* Get a DbMapping for a given prototype name. This is just a proxy
|
||||||
* method to the app's getDbMapping() method.
|
* method to the app's getDbMapping() method.
|
||||||
|
|
|
@ -190,7 +190,7 @@ public final class Property implements IProperty, Serializable, Cloneable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return the property's value in its native class
|
||||||
*/
|
*/
|
||||||
public Object getValue() {
|
public Object getValue() {
|
||||||
return value;
|
return value;
|
||||||
|
@ -199,12 +199,24 @@ public final class Property implements IProperty, Serializable, Cloneable {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return the property's type as defined in helma.objectmodel.IProperty.java
|
||||||
*/
|
*/
|
||||||
public int getType() {
|
public int getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directly set the value of this property.
|
||||||
|
*/
|
||||||
|
protected void setValue(Object value, int type) {
|
||||||
|
if (type == NODE) {
|
||||||
|
unregisterNode();
|
||||||
|
}
|
||||||
|
this.value = value;
|
||||||
|
this.type = type;
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
|
@ -419,7 +431,7 @@ public final class Property implements IProperty, Serializable, Cloneable {
|
||||||
|
|
||||||
case DATE:
|
case DATE:
|
||||||
|
|
||||||
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yy hh:mm:ss");
|
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
|
||||||
|
|
||||||
return format.format((Date) value);
|
return format.format((Date) value);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ import helma.objectmodel.*;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This describes how a property of a persistent Object is stored in a
|
* This describes how a property of a persistent Object is stored in a
|
||||||
|
@ -41,6 +43,10 @@ public final class Relation {
|
||||||
// a 1-to-many relation, a field in another table points to objects of this type
|
// a 1-to-many relation, a field in another table points to objects of this type
|
||||||
public final static int COLLECTION = 2;
|
public final static int COLLECTION = 2;
|
||||||
|
|
||||||
|
// a 1-to-1 reference with multiple or otherwise not-trivial constraints
|
||||||
|
// this is managed differently than REFERENCE, hence the separate type.
|
||||||
|
public final static int COMPLEX_REFERENCE = 3;
|
||||||
|
|
||||||
// direct mapping is a very powerful feature: objects of some types can be directly accessed
|
// direct mapping is a very powerful feature: objects of some types can be directly accessed
|
||||||
// by one of their properties/db fields.
|
// by one of their properties/db fields.
|
||||||
// public final static int DIRECT = 3;
|
// public final static int DIRECT = 3;
|
||||||
|
@ -63,7 +69,8 @@ public final class Relation {
|
||||||
boolean readonly;
|
boolean readonly;
|
||||||
boolean aggressiveLoading;
|
boolean aggressiveLoading;
|
||||||
boolean aggressiveCaching;
|
boolean aggressiveCaching;
|
||||||
boolean isPrivate;
|
boolean isPrivate = false;
|
||||||
|
boolean referencesPrimaryKey = false;
|
||||||
String accessName; // db column used to access objects through this relation
|
String accessName; // db column used to access objects through this relation
|
||||||
String order;
|
String order;
|
||||||
String groupbyOrder;
|
String groupbyOrder;
|
||||||
|
@ -102,6 +109,7 @@ public final class Relation {
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
public void update(String desc, Properties props) {
|
public void update(String desc, Properties props) {
|
||||||
Application app = ownType.getApplication();
|
Application app = ownType.getApplication();
|
||||||
|
boolean notPrimitive = false;
|
||||||
|
|
||||||
if ((desc == null) || "".equals(desc.trim())) {
|
if ((desc == null) || "".equals(desc.trim())) {
|
||||||
if (propName != null) {
|
if (propName != null) {
|
||||||
|
@ -130,7 +138,9 @@ public final class Relation {
|
||||||
prototype = proto;
|
prototype = proto;
|
||||||
} else if ("object".equalsIgnoreCase(ref)) {
|
} else if ("object".equalsIgnoreCase(ref)) {
|
||||||
virtual = false;
|
virtual = false;
|
||||||
|
if (reftype != COMPLEX_REFERENCE) {
|
||||||
reftype = REFERENCE;
|
reftype = REFERENCE;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Invalid property Mapping: " + desc);
|
throw new RuntimeException("Invalid property Mapping: " + desc);
|
||||||
}
|
}
|
||||||
|
@ -141,6 +151,12 @@ public final class Relation {
|
||||||
throw new RuntimeException("DbMapping for " + proto +
|
throw new RuntimeException("DbMapping for " + proto +
|
||||||
" not found from " + ownType.typename);
|
" not found from " + ownType.typename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure the type we're referring to is up to date!
|
||||||
|
if (otherType != null && otherType.needsUpdate()) {
|
||||||
|
otherType.update();
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
virtual = false;
|
virtual = false;
|
||||||
columnName = desc;
|
columnName = desc;
|
||||||
|
@ -148,13 +164,7 @@ public final class Relation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String rdonly = props.getProperty(propName + ".readonly");
|
readonly = "true".equalsIgnoreCase(props.getProperty(propName + ".readonly"));
|
||||||
|
|
||||||
if ((rdonly != null) && "true".equalsIgnoreCase(rdonly)) {
|
|
||||||
readonly = true;
|
|
||||||
} else {
|
|
||||||
readonly = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
isPrivate = "true".equalsIgnoreCase(props.getProperty(propName + ".private"));
|
isPrivate = "true".equalsIgnoreCase(props.getProperty(propName + ".private"));
|
||||||
|
|
||||||
|
@ -167,6 +177,33 @@ public final class Relation {
|
||||||
constraints = new Constraint[newConstraints.size()];
|
constraints = new Constraint[newConstraints.size()];
|
||||||
newConstraints.copyInto(constraints);
|
newConstraints.copyInto(constraints);
|
||||||
|
|
||||||
|
|
||||||
|
if (reftype == REFERENCE || reftype == COMPLEX_REFERENCE) {
|
||||||
|
if (constraints.length == 0) {
|
||||||
|
referencesPrimaryKey = true;
|
||||||
|
} else {
|
||||||
|
boolean rprim = false;
|
||||||
|
for (int i=0; i<constraints.length; i++) {
|
||||||
|
if (constraints[0].foreignKeyIsPrimary()) {
|
||||||
|
rprim = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
referencesPrimaryKey = rprim;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this is a non-trivial reference
|
||||||
|
if (constraints.length > 0 && !usesPrimaryKey()) {
|
||||||
|
reftype = COMPLEX_REFERENCE;
|
||||||
|
} else {
|
||||||
|
reftype = REFERENCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reftype == COLLECTION) {
|
||||||
|
referencesPrimaryKey = (accessName == null) ||
|
||||||
|
accessName.equalsIgnoreCase(otherType.getIDField());
|
||||||
|
}
|
||||||
|
|
||||||
// if DbMapping for virtual nodes has already been created,
|
// if DbMapping for virtual nodes has already been created,
|
||||||
// update its subnode relation.
|
// update its subnode relation.
|
||||||
// FIXME: needs to be synchronized?
|
// FIXME: needs to be synchronized?
|
||||||
|
@ -175,6 +212,8 @@ public final class Relation {
|
||||||
virtualMapping.subRelation = getVirtualSubnodeRelation();
|
virtualMapping.subRelation = getVirtualSubnodeRelation();
|
||||||
virtualMapping.propRelation = getVirtualPropertyRelation();
|
virtualMapping.propRelation = getVirtualPropertyRelation();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
referencesPrimaryKey = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +238,13 @@ public final class Relation {
|
||||||
// get additional filter property
|
// get additional filter property
|
||||||
filter = props.getProperty(propName + ".filter");
|
filter = props.getProperty(propName + ".filter");
|
||||||
|
|
||||||
if ((filter != null) && (filter.trim().length() == 0)) {
|
if (filter != null) {
|
||||||
|
if (filter.trim().length() == 0) {
|
||||||
filter = null;
|
filter = null;
|
||||||
|
} else {
|
||||||
|
// parenthesise filter
|
||||||
|
filter = "("+filter+")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get max size of collection
|
// get max size of collection
|
||||||
|
@ -237,7 +281,7 @@ public final class Relation {
|
||||||
}
|
}
|
||||||
|
|
||||||
// aggressive loading and caching is not supported for groupby-nodes
|
// aggressive loading and caching is not supported for groupby-nodes
|
||||||
aggressiveLoading = aggressiveCaching = false;
|
// aggressiveLoading = aggressiveCaching = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if subnode condition should be applied for property relations
|
// check if subnode condition should be applied for property relations
|
||||||
|
@ -248,9 +292,19 @@ public final class Relation {
|
||||||
String foreign = props.getProperty(propName + ".foreign");
|
String foreign = props.getProperty(propName + ".foreign");
|
||||||
|
|
||||||
if ((local != null) && (foreign != null)) {
|
if ((local != null) && (foreign != null)) {
|
||||||
cnst.addElement(new Constraint(local, otherType.getTableName(), foreign, false));
|
cnst.addElement(new Constraint(local, foreign, false));
|
||||||
columnName = local;
|
columnName = local;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse additional contstraints from *.1 to *.9
|
||||||
|
for (int i=1; i<10; i++) {
|
||||||
|
local = props.getProperty(propName + ".local."+i);
|
||||||
|
foreign = props.getProperty(propName + ".foreign."+i);
|
||||||
|
|
||||||
|
if ((local != null) && (foreign != null)) {
|
||||||
|
cnst.addElement(new Constraint(local, foreign, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -283,6 +337,13 @@ public final class Relation {
|
||||||
return reftype == COLLECTION;
|
return reftype == COLLECTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this Relation describes a complex object reference property
|
||||||
|
*/
|
||||||
|
public boolean isComplexReference() {
|
||||||
|
return reftype == COMPLEX_REFERENCE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell wether the property described by this relation is to be handled as private, i.e.
|
* Tell wether the property described by this relation is to be handled as private, i.e.
|
||||||
* a change on it should not result in any changed object/collection relations.
|
* a change on it should not result in any changed object/collection relations.
|
||||||
|
@ -291,14 +352,30 @@ public final class Relation {
|
||||||
return isPrivate;
|
return isPrivate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether aggressive loading is set for this relation
|
||||||
|
*/
|
||||||
|
public boolean loadAggressively() {
|
||||||
|
return aggressiveLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of constraints for this relation.
|
||||||
|
*/
|
||||||
|
public int countConstraints() {
|
||||||
|
if (constraints == null)
|
||||||
|
return 0;
|
||||||
|
return constraints.length;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the object represented by this Relation has to be
|
* Returns true if the object represented by this Relation has to be
|
||||||
* created dynamically by the Helma objectmodel runtime as a virtual
|
* created dynamically by the Helma objectmodel runtime as a virtual
|
||||||
* node. Virtual nodes are objects which are only generated on demand
|
* node. Virtual nodes are objects which are only generated on demand
|
||||||
* and never stored to a persistent storage.
|
* and never stored to a persistent storage.
|
||||||
*/
|
*/
|
||||||
public boolean createPropertyOnDemand() {
|
public boolean createOnDemand() {
|
||||||
return virtual || (accessName != null) || (groupby != null);
|
return virtual || (accessName != null) || (groupby != null) || isComplexReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -355,6 +432,15 @@ public final class Relation {
|
||||||
return columnType;
|
return columnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the group for a collection relation, if defined.
|
||||||
|
*
|
||||||
|
* @return the name of the column used to group child objects, if any.
|
||||||
|
*/
|
||||||
|
public String getGroup() {
|
||||||
|
return groupby;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a constraint to the current list of constraints
|
* Add a constraint to the current list of constraints
|
||||||
*/
|
*/
|
||||||
|
@ -374,21 +460,11 @@ public final class Relation {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return ...
|
* @return true if the foreign key used for this relation is the
|
||||||
|
* other object's primary key.
|
||||||
*/
|
*/
|
||||||
public boolean usesPrimaryKey() {
|
public boolean usesPrimaryKey() {
|
||||||
if (otherType != null) {
|
return referencesPrimaryKey;
|
||||||
if (reftype == REFERENCE) {
|
|
||||||
return (constraints.length == 1) && constraints[0].foreignKeyIsPrimary();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reftype == COLLECTION) {
|
|
||||||
return (accessName == null) ||
|
|
||||||
accessName.equalsIgnoreCase(otherType.getIDField());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -510,7 +586,7 @@ public final class Relation {
|
||||||
vr.prototype = groupbyPrototype;
|
vr.prototype = groupbyPrototype;
|
||||||
vr.filter = filter;
|
vr.filter = filter;
|
||||||
vr.constraints = constraints;
|
vr.constraints = constraints;
|
||||||
vr.addConstraint(new Constraint(null, null, groupby, true));
|
vr.addConstraint(new Constraint(null, groupby, true));
|
||||||
vr.aggressiveLoading = aggressiveLoading;
|
vr.aggressiveLoading = aggressiveLoading;
|
||||||
vr.aggressiveCaching = aggressiveCaching;
|
vr.aggressiveCaching = aggressiveCaching;
|
||||||
|
|
||||||
|
@ -531,7 +607,7 @@ public final class Relation {
|
||||||
vr.prototype = groupbyPrototype;
|
vr.prototype = groupbyPrototype;
|
||||||
vr.filter = filter;
|
vr.filter = filter;
|
||||||
vr.constraints = constraints;
|
vr.constraints = constraints;
|
||||||
vr.addConstraint(new Constraint(null, null, groupby, true));
|
vr.addConstraint(new Constraint(null, groupby, true));
|
||||||
|
|
||||||
return vr;
|
return vr;
|
||||||
}
|
}
|
||||||
|
@ -545,11 +621,13 @@ public final class Relation {
|
||||||
StringBuffer q = new StringBuffer();
|
StringBuffer q = new StringBuffer();
|
||||||
String prefix = pre;
|
String prefix = pre;
|
||||||
|
|
||||||
if (kstr != null) {
|
if (kstr != null && !isComplexReference()) {
|
||||||
q.append(prefix);
|
q.append(prefix);
|
||||||
|
|
||||||
String accessColumn = (accessName == null) ? otherType.getIDField() : accessName;
|
String accessColumn = (accessName == null) ? otherType.getIDField() : accessName;
|
||||||
|
|
||||||
|
q.append(otherType.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(accessColumn);
|
q.append(accessColumn);
|
||||||
q.append(" = ");
|
q.append(" = ");
|
||||||
|
|
||||||
|
@ -602,21 +680,39 @@ public final class Relation {
|
||||||
public String renderConstraints(INode home, INode nonvirtual)
|
public String renderConstraints(INode home, INode nonvirtual)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
StringBuffer q = new StringBuffer();
|
StringBuffer q = new StringBuffer();
|
||||||
String suffix = " AND ";
|
String prefix = " AND ";
|
||||||
|
|
||||||
for (int i = 0; i < constraints.length; i++) {
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
|
q.append(prefix);
|
||||||
constraints[i].addToQuery(q, home, nonvirtual);
|
constraints[i].addToQuery(q, home, nonvirtual);
|
||||||
q.append(suffix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter != null) {
|
if (filter != null) {
|
||||||
|
q.append(prefix);
|
||||||
q.append(filter);
|
q.append(filter);
|
||||||
q.append(suffix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.toString();
|
return q.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void renderJoinConstraints(StringBuffer select) {
|
||||||
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
|
select.append(ownType.getTableName());
|
||||||
|
select.append(".");
|
||||||
|
select.append(constraints[i].localName);
|
||||||
|
select.append(" = _HLM_");
|
||||||
|
select.append(propName);
|
||||||
|
select.append(".");
|
||||||
|
select.append(constraints[i].foreignName);
|
||||||
|
if (i == constraints.length-1) {
|
||||||
|
select.append(" ");
|
||||||
|
} else {
|
||||||
|
select.append(" AND ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the order section to use for this relation
|
* Get the order section to use for this relation
|
||||||
*/
|
*/
|
||||||
|
@ -656,16 +752,14 @@ public final class Relation {
|
||||||
if (propname != null) {
|
if (propname != null) {
|
||||||
INode home = constraints[i].isGroupby ? parent
|
INode home = constraints[i].isGroupby ? parent
|
||||||
: parent.getNonVirtualParent();
|
: parent.getNonVirtualParent();
|
||||||
String localName = constraints[i].localName;
|
|
||||||
String value = null;
|
String value = null;
|
||||||
|
|
||||||
if ((localName == null) ||
|
if (constraints[i].localKeyIsPrimary(home.getDbMapping())) {
|
||||||
localName.equalsIgnoreCase(ownType.getIDField())) {
|
|
||||||
value = home.getID();
|
value = home.getID();
|
||||||
} else if (ownType.isRelational()) {
|
} else if (ownType.isRelational()) {
|
||||||
value = home.getString(ownType.columnNameToProperty(localName));
|
value = home.getString(constraints[i].localProperty());
|
||||||
} else {
|
} else {
|
||||||
value = home.getString(localName);
|
value = home.getString(constraints[i].localName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((value != null) && !value.equals(child.getString(propname))) {
|
if ((value != null) && !value.equals(child.getString(propname))) {
|
||||||
|
@ -682,8 +776,7 @@ public final class Relation {
|
||||||
* appropriate properties
|
* appropriate properties
|
||||||
*/
|
*/
|
||||||
public void setConstraints(Node parent, Node child) {
|
public void setConstraints(Node parent, Node child) {
|
||||||
INode home = parent.getNonVirtualParent();
|
Node home = parent.getNonVirtualParent();
|
||||||
|
|
||||||
for (int i = 0; i < constraints.length; i++) {
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
// don't set groupby constraints since we don't know if the
|
// don't set groupby constraints since we don't know if the
|
||||||
// parent node is the base node or a group node
|
// parent node is the base node or a group node
|
||||||
|
@ -691,14 +784,26 @@ public final class Relation {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Relation crel = otherType.columnNameToRelation(constraints[i].foreignName);
|
// check if we update the local or the other object, depending on
|
||||||
|
// whether the primary key of either side is used.
|
||||||
|
|
||||||
|
if (constraints[i].foreignKeyIsPrimary()) {
|
||||||
|
String localProp = constraints[i].localProperty();
|
||||||
|
if (localProp == null) {
|
||||||
|
System.err.println ("Error: column "+constraints[i].localName+
|
||||||
|
" must be mapped in order to be used as constraint in "+
|
||||||
|
Relation.this);
|
||||||
|
} else {
|
||||||
|
home.setString(localProp, child.getID());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Relation crel = otherType.columnNameToRelation(constraints[i].foreignName);
|
||||||
if (crel != null) {
|
if (crel != null) {
|
||||||
// INode home = constraints[i].isGroupby ? parent : nonVirtual;
|
// INode home = constraints[i].isGroupby ? parent : nonVirtual;
|
||||||
String localName = constraints[i].localName;
|
|
||||||
|
|
||||||
if ((localName == null) ||
|
if (constraints[i].localKeyIsPrimary(home.getDbMapping())) {
|
||||||
localName.equalsIgnoreCase(ownType.getIDField())) {
|
|
||||||
// only set node if property in child object is defined as reference.
|
// only set node if property in child object is defined as reference.
|
||||||
if (crel.reftype == REFERENCE) {
|
if (crel.reftype == REFERENCE) {
|
||||||
INode currentValue = child.getNode(crel.propName);
|
INode currentValue = child.getNode(crel.propName);
|
||||||
|
@ -717,22 +822,92 @@ public final class Relation {
|
||||||
child.setString(crel.propName, home.getID());
|
child.setString(crel.propName, home.getID());
|
||||||
}
|
}
|
||||||
} else if (crel.reftype == PRIMITIVE) {
|
} else if (crel.reftype == PRIMITIVE) {
|
||||||
String value = null;
|
Property prop = null;
|
||||||
|
|
||||||
if (ownType.isRelational()) {
|
if (ownType.isRelational()) {
|
||||||
value = home.getString(ownType.columnNameToProperty(localName));
|
prop = home.getProperty(constraints[i].localProperty());
|
||||||
} else {
|
} else {
|
||||||
value = home.getString(localName);
|
prop = home.getProperty(constraints[i].localName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value != null) {
|
if (prop != null) {
|
||||||
child.setString(crel.propName, value);
|
child.set(crel.propName, prop.getValue(), prop.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unset the constraints that link two objects together.
|
||||||
|
*/
|
||||||
|
public void unsetConstraints(Node parent, INode child) {
|
||||||
|
Node home = parent.getNonVirtualParent();
|
||||||
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
|
// don't set groupby constraints since we don't know if the
|
||||||
|
// parent node is the base node or a group node
|
||||||
|
if (constraints[i].isGroupby) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we update the local or the other object, depending on
|
||||||
|
// whether the primary key of either side is used.
|
||||||
|
|
||||||
|
if (constraints[i].foreignKeyIsPrimary()) {
|
||||||
|
String localProp = constraints[i].localProperty();
|
||||||
|
if (localProp != null) {
|
||||||
|
home.setString(localProp, null);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Relation crel = otherType.columnNameToRelation(constraints[i].foreignName);
|
||||||
|
if (crel != null) {
|
||||||
|
// INode home = constraints[i].isGroupby ? parent : nonVirtual;
|
||||||
|
|
||||||
|
if (constraints[i].localKeyIsPrimary(home.getDbMapping())) {
|
||||||
|
// only set node if property in child object is defined as reference.
|
||||||
|
if (crel.reftype == REFERENCE) {
|
||||||
|
INode currentValue = child.getNode(crel.propName);
|
||||||
|
|
||||||
|
if ((currentValue == home)) {
|
||||||
|
child.setString(crel.propName, null);
|
||||||
|
}
|
||||||
|
} else if (crel.reftype == PRIMITIVE) {
|
||||||
|
child.setString(crel.propName, null);
|
||||||
|
}
|
||||||
|
} else if (crel.reftype == PRIMITIVE) {
|
||||||
|
Property prop = null;
|
||||||
|
|
||||||
|
if (ownType.isRelational()) {
|
||||||
|
prop = home.getProperty(constraints[i].localProperty());
|
||||||
|
} else {
|
||||||
|
prop = home.getProperty(constraints[i].localName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop != null) {
|
||||||
|
child.setString(crel.propName, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map containing the key/value pairs for a specific Node
|
||||||
|
*/
|
||||||
|
public Map getKeyParts(INode home) {
|
||||||
|
Map map = new HashMap();
|
||||||
|
for (int i=0; i<constraints.length; i++) {
|
||||||
|
if (ownType.getIDField().equals(constraints[i].localName)) {
|
||||||
|
map.put(constraints[i].foreignName, home.getID());
|
||||||
|
} else {
|
||||||
|
map.put(constraints[i].foreignName, home.getString(constraints[i].localProperty()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
// a utility method to escape single quotes
|
// a utility method to escape single quotes
|
||||||
String escape(String str) {
|
String escape(String str) {
|
||||||
if (str == null) {
|
if (str == null) {
|
||||||
|
@ -766,13 +941,20 @@ public final class Relation {
|
||||||
*/
|
*/
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String c = "";
|
String c = "";
|
||||||
|
String spacer = "";
|
||||||
|
|
||||||
if (constraints != null) {
|
if (constraints != null) {
|
||||||
for (int i = 0; i < constraints.length; i++)
|
c = " constraints: ";
|
||||||
|
for (int i = 0; i < constraints.length; i++) {
|
||||||
|
c += spacer;
|
||||||
c += constraints[i].toString();
|
c += constraints[i].toString();
|
||||||
|
spacer = ", ";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Relation[" + ownType + "." + propName + ">" + otherType + "]" + c;
|
String target = otherType == null ? columnName : otherType.toString();
|
||||||
|
|
||||||
|
return "Relation " + ownType+"."+propName + " -> " + target + c;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -781,13 +963,11 @@ public final class Relation {
|
||||||
*/
|
*/
|
||||||
class Constraint {
|
class Constraint {
|
||||||
String localName;
|
String localName;
|
||||||
String tableName;
|
|
||||||
String foreignName;
|
String foreignName;
|
||||||
boolean isGroupby;
|
boolean isGroupby;
|
||||||
|
|
||||||
Constraint(String local, String table, String foreign, boolean groupby) {
|
Constraint(String local, String foreign, boolean groupby) {
|
||||||
localName = local;
|
localName = local;
|
||||||
tableName = table;
|
|
||||||
foreignName = foreign;
|
foreignName = foreign;
|
||||||
isGroupby = groupby;
|
isGroupby = groupby;
|
||||||
}
|
}
|
||||||
|
@ -806,6 +986,8 @@ public final class Relation {
|
||||||
local = ref.getString(homeprop);
|
local = ref.getString(homeprop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
q.append(otherType.getTableName());
|
||||||
|
q.append(".");
|
||||||
q.append(foreignName);
|
q.append(foreignName);
|
||||||
q.append(" = ");
|
q.append(" = ");
|
||||||
|
|
||||||
|
@ -823,6 +1005,11 @@ public final class Relation {
|
||||||
foreignName.equalsIgnoreCase(otherType.getIDField());
|
foreignName.equalsIgnoreCase(otherType.getIDField());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean localKeyIsPrimary(DbMapping homeMapping) {
|
||||||
|
return (homeMapping == null) || (localName == null) ||
|
||||||
|
localName.equalsIgnoreCase(homeMapping.getIDField());
|
||||||
|
}
|
||||||
|
|
||||||
public String foreignProperty() {
|
public String foreignProperty() {
|
||||||
return otherType.columnNameToProperty(foreignName);
|
return otherType.columnNameToProperty(foreignName);
|
||||||
}
|
}
|
||||||
|
@ -832,7 +1019,7 @@ public final class Relation {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return ownType + "." + localName + "=" + tableName + "." + foreignName;
|
return localName + "=" + otherType.getTypeName() + "." + foreignName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,7 +238,7 @@ public class ESNode extends ObjectPrototype {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(what[1] instanceof ESNode)) {
|
if (!(what[1] instanceof ESNode)) {
|
||||||
throw new EcmaScriptException("Can ony add Node objects as subnodes");
|
throw new EcmaScriptException("Can only add Node objects as subnodes");
|
||||||
}
|
}
|
||||||
|
|
||||||
ESNode esn = (ESNode) what[1];
|
ESNode esn = (ESNode) what[1];
|
||||||
|
@ -341,23 +341,6 @@ public class ESNode extends ObjectPrototype {
|
||||||
((Node) node).prefetchChildren(start, length);
|
((Node) node).prefetchChildren(start, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This used to be different from add(), it isn't anymore. It's left here for
|
|
||||||
* compatibility.
|
|
||||||
*/
|
|
||||||
public boolean link(ESValue[] args) {
|
|
||||||
checkNode();
|
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
if (args[i] instanceof ESNode) {
|
|
||||||
ESNode esn = (ESNode) args[i];
|
|
||||||
|
|
||||||
node.addNode(esn.getNode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
@ -107,7 +107,6 @@ public final class HopExtension {
|
||||||
esNodePrototype.putHiddenProperty("addAt", new NodeAddAt("addAt", evaluator, fp));
|
esNodePrototype.putHiddenProperty("addAt", new NodeAddAt("addAt", evaluator, fp));
|
||||||
esNodePrototype.putHiddenProperty("remove",
|
esNodePrototype.putHiddenProperty("remove",
|
||||||
new NodeRemove("remove", evaluator, fp));
|
new NodeRemove("remove", evaluator, fp));
|
||||||
esNodePrototype.putHiddenProperty("link", new NodeLink("link", evaluator, fp));
|
|
||||||
esNodePrototype.putHiddenProperty("list", new NodeList("list", evaluator, fp));
|
esNodePrototype.putHiddenProperty("list", new NodeList("list", evaluator, fp));
|
||||||
esNodePrototype.putHiddenProperty("set", new NodeSet("set", evaluator, fp));
|
esNodePrototype.putHiddenProperty("set", new NodeSet("set", evaluator, fp));
|
||||||
esNodePrototype.putHiddenProperty("get", new NodeGet("get", evaluator, fp));
|
esNodePrototype.putHiddenProperty("get", new NodeGet("get", evaluator, fp));
|
||||||
|
@ -270,19 +269,6 @@ public final class HopExtension {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NodeLink extends BuiltinFunctionObject {
|
|
||||||
NodeLink(String name, Evaluator evaluator, FunctionPrototype fp) {
|
|
||||||
super(fp, evaluator, name, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
|
|
||||||
throws EcmaScriptException {
|
|
||||||
ESNode node = (ESNode) thisObject;
|
|
||||||
|
|
||||||
return ESBoolean.makeBoolean(node.link(arguments));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NodeList extends BuiltinFunctionObject {
|
class NodeList extends BuiltinFunctionObject {
|
||||||
NodeList(String name, Evaluator evaluator, FunctionPrototype fp) {
|
NodeList(String name, Evaluator evaluator, FunctionPrototype fp) {
|
||||||
super(fp, evaluator, name, 0);
|
super(fp, evaluator, name, 0);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
/* Portierung von helma.asp.AspClient auf Servlets */
|
/* Portierung von helma.asp.AspClient auf Servlets */
|
||||||
/* Author: Raphael Spannocchi Datum: 27.11.1998 */
|
/* Author: Raphael Spannocchi Datum: 27.11.1998 */
|
||||||
|
|
||||||
package helma.servlet;
|
package helma.servlet;
|
||||||
|
|
||||||
import helma.framework.*;
|
import helma.framework.*;
|
||||||
|
@ -292,6 +293,7 @@ public abstract class AbstractServletClient extends HttpServlet {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
sendError(response, response.SC_INTERNAL_SERVER_ERROR,
|
sendError(response, response.SC_INTERNAL_SERVER_ERROR,
|
||||||
"Error in request handler:" + x);
|
"Error in request handler:" + x);
|
||||||
|
x.printStackTrace();
|
||||||
} else {
|
} else {
|
||||||
sendError(response, response.SC_INTERNAL_SERVER_ERROR,
|
sendError(response, response.SC_INTERNAL_SERVER_ERROR,
|
||||||
"The server encountered an error while processing your request. " +
|
"The server encountered an error while processing your request. " +
|
||||||
|
|
|
@ -32,9 +32,6 @@ public final class EmbeddedServletClient extends AbstractServletClient {
|
||||||
private Application app = null;
|
private Application app = null;
|
||||||
private String appName;
|
private String appName;
|
||||||
|
|
||||||
// The path where this servlet is mounted
|
|
||||||
String mountpoint;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new EmbeddedServletClient object.
|
* Creates a new EmbeddedServletClient object.
|
||||||
*/
|
*/
|
||||||
|
@ -56,12 +53,6 @@ public final class EmbeddedServletClient extends AbstractServletClient {
|
||||||
if (appName == null) {
|
if (appName == null) {
|
||||||
throw new ServletException("Application name not set in init parameters");
|
throw new ServletException("Application name not set in init parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
mountpoint = init.getInitParameter("mountpoint");
|
|
||||||
|
|
||||||
if (mountpoint == null) {
|
|
||||||
mountpoint = "/" + appName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResponseTrans execute(RequestTrans req) throws Exception {
|
ResponseTrans execute(RequestTrans req) throws Exception {
|
||||||
|
|
|
@ -72,17 +72,18 @@ import java.util.*;
|
||||||
|
|
||||||
public class CronJob {
|
public class CronJob {
|
||||||
|
|
||||||
// used as the value in hashtables
|
private static HashSet all = new HashSet (2);
|
||||||
private static Object value = new Object();
|
|
||||||
private static Hashtable all = new Hashtable ();
|
|
||||||
private static String ALL_VALUE = "*";
|
private static String ALL_VALUE = "*";
|
||||||
|
static {
|
||||||
|
all.add (ALL_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
private Hashtable year;
|
private HashSet year;
|
||||||
private Hashtable month;
|
private HashSet month;
|
||||||
private Hashtable day;
|
private HashSet day;
|
||||||
private Hashtable weekday;
|
private HashSet weekday;
|
||||||
private Hashtable hour;
|
private HashSet hour;
|
||||||
private Hashtable minute;
|
private HashSet minute;
|
||||||
|
|
||||||
private String name = null;
|
private String name = null;
|
||||||
private String function = null;
|
private String function = null;
|
||||||
|
@ -161,21 +162,22 @@ public class CronJob {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
public static CronJob newJob (String functionName, String year, String month, String day, String weekday, String hour, String minute) {
|
public static CronJob newJob (String functionName, String year, String month,
|
||||||
|
String day, String weekday, String hour, String minute) {
|
||||||
CronJob job = new CronJob (functionName);
|
CronJob job = new CronJob (functionName);
|
||||||
job.setFunction (functionName);
|
job.setFunction (functionName);
|
||||||
if (year != null)
|
if (year != null)
|
||||||
parseYear (job, year);
|
job.parseYear (year);
|
||||||
if (month != null)
|
if (month != null)
|
||||||
parseMonth (job, month);
|
job.parseMonth (month);
|
||||||
if (day != null)
|
if (day != null)
|
||||||
parseDay (job, day);
|
job.parseDay (day);
|
||||||
if (weekday != null)
|
if (weekday != null)
|
||||||
parseWeekDay (job, weekday);
|
job.parseWeekDay (weekday);
|
||||||
if (hour != null)
|
if (hour != null)
|
||||||
parseHour (job, hour);
|
job.parseHour (hour);
|
||||||
if (minute != null)
|
if (minute != null)
|
||||||
parseMinute (job, minute);
|
job.parseMinute (minute);
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,19 +205,19 @@ public class CronJob {
|
||||||
if (jobSpec.equalsIgnoreCase("function")) {
|
if (jobSpec.equalsIgnoreCase("function")) {
|
||||||
job.setFunction(value);
|
job.setFunction(value);
|
||||||
} else if (jobSpec.equalsIgnoreCase("year")) {
|
} else if (jobSpec.equalsIgnoreCase("year")) {
|
||||||
parseYear (job, value);
|
job.parseYear (value);
|
||||||
} else if (jobSpec.equalsIgnoreCase("month")) {
|
} else if (jobSpec.equalsIgnoreCase("month")) {
|
||||||
parseMonth (job, value);
|
job.parseMonth (value);
|
||||||
} else if (jobSpec.equalsIgnoreCase("day")) {
|
} else if (jobSpec.equalsIgnoreCase("day")) {
|
||||||
parseDay (job, value);
|
job.parseDay (value);
|
||||||
} else if (jobSpec.equalsIgnoreCase("weekday")) {
|
} else if (jobSpec.equalsIgnoreCase("weekday")) {
|
||||||
parseWeekDay (job, value);
|
job.parseWeekDay (value);
|
||||||
} else if (jobSpec.equalsIgnoreCase("hour")) {
|
} else if (jobSpec.equalsIgnoreCase("hour")) {
|
||||||
parseHour (job, value);
|
job.parseHour (value);
|
||||||
} else if (jobSpec.equalsIgnoreCase("minute")) {
|
} else if (jobSpec.equalsIgnoreCase("minute")) {
|
||||||
parseMinute (job, value);
|
job.parseMinute (value);
|
||||||
} else if (jobSpec.equalsIgnoreCase("timeout")) {
|
} else if (jobSpec.equalsIgnoreCase("timeout")) {
|
||||||
parseTimeout (job, value);
|
job.parseTimeout (value);
|
||||||
}
|
}
|
||||||
} catch (NoSuchElementException nsee) {
|
} catch (NoSuchElementException nsee) {
|
||||||
}
|
}
|
||||||
|
@ -249,9 +251,9 @@ public class CronJob {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void parseYear (CronJob job, String value) {
|
public void parseYear (String value) {
|
||||||
if (value.equals("*")) {
|
if (value.equals("*")) {
|
||||||
job.setAllYears(true);
|
setAllYears(true);
|
||||||
} else {
|
} else {
|
||||||
StringTokenizer st = new StringTokenizer(value.trim(), ",");
|
StringTokenizer st = new StringTokenizer(value.trim(), ",");
|
||||||
while (st.hasMoreTokens()) {
|
while (st.hasMoreTokens()) {
|
||||||
|
@ -260,54 +262,54 @@ public class CronJob {
|
||||||
int start = Integer.parseInt(s.substring(0, s.indexOf("-")));
|
int start = Integer.parseInt(s.substring(0, s.indexOf("-")));
|
||||||
int finish = Integer.parseInt(s.substring(s.indexOf("-") +1));
|
int finish = Integer.parseInt(s.substring(s.indexOf("-") +1));
|
||||||
for (int i=start; i<=finish; i++) {
|
for (int i=start; i<=finish; i++) {
|
||||||
job.addYear(i);
|
addYear(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int y = Integer.parseInt(s);
|
int y = Integer.parseInt(s);
|
||||||
job.addYear(y);
|
addYear(y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void parseMonth (CronJob job, String value) {
|
public void parseMonth (String value) {
|
||||||
if (value.equals("*")) {
|
if (value.equals("*")) {
|
||||||
job.setAllMonths(true);
|
setAllMonths(true);
|
||||||
} else {
|
} else {
|
||||||
StringTokenizer st = new StringTokenizer(value.trim(), ",");
|
StringTokenizer st = new StringTokenizer(value.trim(), ",");
|
||||||
while (st.hasMoreTokens()) {
|
while (st.hasMoreTokens()) {
|
||||||
String m = st.nextToken();
|
String m = st.nextToken();
|
||||||
if (m.equalsIgnoreCase("january"))
|
if (m.equalsIgnoreCase("january"))
|
||||||
job.addMonth(Calendar.JANUARY);
|
addMonth(Calendar.JANUARY);
|
||||||
if (m.equalsIgnoreCase("february"))
|
if (m.equalsIgnoreCase("february"))
|
||||||
job.addMonth(Calendar.FEBRUARY);
|
addMonth(Calendar.FEBRUARY);
|
||||||
if (m.equalsIgnoreCase("march"))
|
if (m.equalsIgnoreCase("march"))
|
||||||
job.addMonth(Calendar.MARCH);
|
addMonth(Calendar.MARCH);
|
||||||
if (m.equalsIgnoreCase("april"))
|
if (m.equalsIgnoreCase("april"))
|
||||||
job.addMonth(Calendar.APRIL);
|
addMonth(Calendar.APRIL);
|
||||||
if (m.equalsIgnoreCase("may"))
|
if (m.equalsIgnoreCase("may"))
|
||||||
job.addMonth(Calendar.MAY);
|
addMonth(Calendar.MAY);
|
||||||
if (m.equalsIgnoreCase("june"))
|
if (m.equalsIgnoreCase("june"))
|
||||||
job.addMonth(Calendar.JUNE);
|
addMonth(Calendar.JUNE);
|
||||||
if (m.equalsIgnoreCase("july"))
|
if (m.equalsIgnoreCase("july"))
|
||||||
job.addMonth(Calendar.JULY);
|
addMonth(Calendar.JULY);
|
||||||
if (m.equalsIgnoreCase("august"))
|
if (m.equalsIgnoreCase("august"))
|
||||||
job.addMonth(Calendar.AUGUST);
|
addMonth(Calendar.AUGUST);
|
||||||
if (m.equalsIgnoreCase("september"))
|
if (m.equalsIgnoreCase("september"))
|
||||||
job.addMonth(Calendar.SEPTEMBER);
|
addMonth(Calendar.SEPTEMBER);
|
||||||
if (m.equalsIgnoreCase("october"))
|
if (m.equalsIgnoreCase("october"))
|
||||||
job.addMonth(Calendar.OCTOBER);
|
addMonth(Calendar.OCTOBER);
|
||||||
if (m.equalsIgnoreCase("november"))
|
if (m.equalsIgnoreCase("november"))
|
||||||
job.addMonth(Calendar.NOVEMBER);
|
addMonth(Calendar.NOVEMBER);
|
||||||
if (m.equalsIgnoreCase("december"))
|
if (m.equalsIgnoreCase("december"))
|
||||||
job.addMonth(Calendar.DECEMBER);
|
addMonth(Calendar.DECEMBER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void parseDay (CronJob job, String day) {
|
public void parseDay (String day) {
|
||||||
if (day.equals("*")) {
|
if (day.equals("*")) {
|
||||||
job.setAllDays(true);
|
setAllDays(true);
|
||||||
} else {
|
} else {
|
||||||
StringTokenizer st = new StringTokenizer(day.trim(), ",");
|
StringTokenizer st = new StringTokenizer(day.trim(), ",");
|
||||||
while (st.hasMoreTokens()) {
|
while (st.hasMoreTokens()) {
|
||||||
|
@ -316,46 +318,46 @@ public class CronJob {
|
||||||
int start = Integer.parseInt(s.substring(0, s.indexOf("-")));
|
int start = Integer.parseInt(s.substring(0, s.indexOf("-")));
|
||||||
int finish = Integer.parseInt(s.substring(s.indexOf("-") +1));
|
int finish = Integer.parseInt(s.substring(s.indexOf("-") +1));
|
||||||
for (int i=start; i<=finish; i++) {
|
for (int i=start; i<=finish; i++) {
|
||||||
job.addDay(i);
|
addDay(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int d = Integer.parseInt(s);
|
int d = Integer.parseInt(s);
|
||||||
job.addDay(d);
|
addDay(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void parseWeekDay (CronJob job, String weekday) {
|
public void parseWeekDay (String weekday) {
|
||||||
if (weekday.equals("*")) {
|
if (weekday.equals("*")) {
|
||||||
job.setAllWeekdays(true);
|
setAllWeekdays(true);
|
||||||
} else {
|
} else {
|
||||||
StringTokenizer st = new StringTokenizer(weekday.trim(), ",");
|
StringTokenizer st = new StringTokenizer(weekday.trim(), ",");
|
||||||
while (st.hasMoreTokens()) {
|
while (st.hasMoreTokens()) {
|
||||||
String d = st.nextToken();
|
String d = st.nextToken();
|
||||||
if (d.equalsIgnoreCase("monday"))
|
if (d.equalsIgnoreCase("monday"))
|
||||||
job.addWeekday(Calendar.MONDAY);
|
addWeekday(Calendar.MONDAY);
|
||||||
if (d.equalsIgnoreCase("tuesday"))
|
if (d.equalsIgnoreCase("tuesday"))
|
||||||
job.addWeekday(Calendar.TUESDAY);
|
addWeekday(Calendar.TUESDAY);
|
||||||
if (d.equalsIgnoreCase("wednesday"))
|
if (d.equalsIgnoreCase("wednesday"))
|
||||||
job.addWeekday(Calendar.WEDNESDAY);
|
addWeekday(Calendar.WEDNESDAY);
|
||||||
if (d.equalsIgnoreCase("thursday"))
|
if (d.equalsIgnoreCase("thursday"))
|
||||||
job.addWeekday(Calendar.THURSDAY);
|
addWeekday(Calendar.THURSDAY);
|
||||||
if (d.equalsIgnoreCase("friday"))
|
if (d.equalsIgnoreCase("friday"))
|
||||||
job.addWeekday(Calendar.FRIDAY);
|
addWeekday(Calendar.FRIDAY);
|
||||||
if (d.equalsIgnoreCase("saturday"))
|
if (d.equalsIgnoreCase("saturday"))
|
||||||
job.addWeekday(Calendar.SATURDAY);
|
addWeekday(Calendar.SATURDAY);
|
||||||
if (d.equalsIgnoreCase("sunday"))
|
if (d.equalsIgnoreCase("sunday"))
|
||||||
job.addWeekday(Calendar.SUNDAY);
|
addWeekday(Calendar.SUNDAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void parseHour (CronJob job, String hour) {
|
public void parseHour (String hour) {
|
||||||
if (hour.equals("*")) {
|
if (hour.equals("*")) {
|
||||||
job.setAllHours(true);
|
setAllHours(true);
|
||||||
} else {
|
} else {
|
||||||
StringTokenizer st = new StringTokenizer(hour.trim (), ",");
|
StringTokenizer st = new StringTokenizer(hour.trim (), ",");
|
||||||
while (st.hasMoreTokens()) {
|
while (st.hasMoreTokens()) {
|
||||||
|
@ -364,20 +366,20 @@ public class CronJob {
|
||||||
int start = Integer.parseInt(s.substring(0, s.indexOf("-")));
|
int start = Integer.parseInt(s.substring(0, s.indexOf("-")));
|
||||||
int finish = Integer.parseInt(s.substring(s.indexOf("-") +1));
|
int finish = Integer.parseInt(s.substring(s.indexOf("-") +1));
|
||||||
for (int i=start; i<=finish; i++) {
|
for (int i=start; i<=finish; i++) {
|
||||||
job.addHour(i);
|
addHour(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int h = Integer.parseInt(s);
|
int h = Integer.parseInt(s);
|
||||||
job.addHour(h);
|
addHour(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void parseMinute (CronJob job, String minute) {
|
public void parseMinute (String minute) {
|
||||||
if (minute.equals("*")) {
|
if (minute.equals("*")) {
|
||||||
job.setAllMinutes(true);
|
setAllMinutes(true);
|
||||||
} else {
|
} else {
|
||||||
StringTokenizer st = new StringTokenizer(minute.trim (), ",");
|
StringTokenizer st = new StringTokenizer(minute.trim (), ",");
|
||||||
while (st.hasMoreTokens()) {
|
while (st.hasMoreTokens()) {
|
||||||
|
@ -386,20 +388,19 @@ public class CronJob {
|
||||||
int start = Integer.parseInt(s.substring(0, s.indexOf("-")));
|
int start = Integer.parseInt(s.substring(0, s.indexOf("-")));
|
||||||
int finish = Integer.parseInt(s.substring(s.indexOf("-") +1));
|
int finish = Integer.parseInt(s.substring(s.indexOf("-") +1));
|
||||||
for (int i=start; i<=finish; i++) {
|
for (int i=start; i<=finish; i++) {
|
||||||
job.addMinute(i);
|
addMinute(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int m = Integer.parseInt(s);
|
int m = Integer.parseInt(s);
|
||||||
job.addMinute(m);
|
addMinute(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void parseTimeout (String timeout) {
|
||||||
public static void parseTimeout (CronJob job, String timeout) {
|
|
||||||
long timeoutValue = 1000 * Long.valueOf(timeout).longValue ();
|
long timeoutValue = 1000 * Long.valueOf(timeout).longValue ();
|
||||||
job.setTimeout (timeoutValue);
|
setTimeout (timeoutValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long nextFullMinute () {
|
public static long nextFullMinute () {
|
||||||
|
@ -419,13 +420,12 @@ public class CronJob {
|
||||||
*/
|
*/
|
||||||
public CronJob (String name) {
|
public CronJob (String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
all.put (ALL_VALUE, value);
|
year = new HashSet (all);
|
||||||
year = new Hashtable (all);
|
month = new HashSet (all);
|
||||||
month = new Hashtable (all);
|
day = new HashSet (all);
|
||||||
day = new Hashtable (all);
|
weekday = new HashSet (all);
|
||||||
weekday = new Hashtable (all);
|
hour = new HashSet (all);
|
||||||
hour = new Hashtable (all);
|
minute = new HashSet (all);
|
||||||
minute = new Hashtable (all);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -439,27 +439,27 @@ public class CronJob {
|
||||||
|
|
||||||
// try and short-circuit as fast as possible.
|
// try and short-circuit as fast as possible.
|
||||||
Integer theYear = new Integer(cal.get(Calendar.YEAR));
|
Integer theYear = new Integer(cal.get(Calendar.YEAR));
|
||||||
if (!year.containsKey(ALL_VALUE) && !year.containsKey(theYear))
|
if (!year.contains(ALL_VALUE) && !year.contains(theYear))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Integer theMonth = new Integer(cal.get(Calendar.MONTH));
|
Integer theMonth = new Integer(cal.get(Calendar.MONTH));
|
||||||
if (!month.containsKey(ALL_VALUE) && !month.containsKey(theMonth))
|
if (!month.contains(ALL_VALUE) && !month.contains(theMonth))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Integer theDay = new Integer(cal.get(Calendar.DAY_OF_MONTH));
|
Integer theDay = new Integer(cal.get(Calendar.DAY_OF_MONTH));
|
||||||
if (!day.containsKey(ALL_VALUE) && !day.containsKey(theDay))
|
if (!day.contains(ALL_VALUE) && !day.contains(theDay))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Integer theWeekDay = new Integer(cal.get(Calendar.DAY_OF_WEEK));
|
Integer theWeekDay = new Integer(cal.get(Calendar.DAY_OF_WEEK));
|
||||||
if (!weekday.containsKey(ALL_VALUE) && !weekday.containsKey(theWeekDay))
|
if (!weekday.contains(ALL_VALUE) && !weekday.contains(theWeekDay))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Integer theHour = new Integer(cal.get(Calendar.HOUR_OF_DAY));
|
Integer theHour = new Integer(cal.get(Calendar.HOUR_OF_DAY));
|
||||||
if (!hour.containsKey(ALL_VALUE) && !hour.containsKey(theHour))
|
if (!hour.contains(ALL_VALUE) && !hour.contains(theHour))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Integer theMinute = new Integer(cal.get(Calendar.MINUTE));
|
Integer theMinute = new Integer(cal.get(Calendar.MINUTE));
|
||||||
if (!minute.containsKey(ALL_VALUE) && !minute.containsKey(theMinute))
|
if (!minute.contains(ALL_VALUE) && !minute.contains(theMinute))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -472,7 +472,7 @@ public class CronJob {
|
||||||
public void addYear(int year)
|
public void addYear(int year)
|
||||||
{
|
{
|
||||||
this.year.remove(ALL_VALUE);
|
this.year.remove(ALL_VALUE);
|
||||||
this.year.put(new Integer(year), value);
|
this.year.add(new Integer(year));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -494,7 +494,7 @@ public class CronJob {
|
||||||
public void setAllYears(boolean set)
|
public void setAllYears(boolean set)
|
||||||
{
|
{
|
||||||
if (set)
|
if (set)
|
||||||
this.year.put(ALL_VALUE, value);
|
this.year.add(ALL_VALUE);
|
||||||
else
|
else
|
||||||
this.year.remove(ALL_VALUE);
|
this.year.remove(ALL_VALUE);
|
||||||
}
|
}
|
||||||
|
@ -508,7 +508,7 @@ public class CronJob {
|
||||||
public void addMonth(int month)
|
public void addMonth(int month)
|
||||||
{
|
{
|
||||||
this.month.remove(ALL_VALUE);
|
this.month.remove(ALL_VALUE);
|
||||||
this.month.put(new Integer(month), value);
|
this.month.add(new Integer(month));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -532,7 +532,7 @@ public class CronJob {
|
||||||
public void setAllMonths(boolean set)
|
public void setAllMonths(boolean set)
|
||||||
{
|
{
|
||||||
if (set)
|
if (set)
|
||||||
this.month.put(ALL_VALUE, value);
|
this.month.add(ALL_VALUE);
|
||||||
else
|
else
|
||||||
this.month.remove(ALL_VALUE);
|
this.month.remove(ALL_VALUE);
|
||||||
}
|
}
|
||||||
|
@ -544,7 +544,7 @@ public class CronJob {
|
||||||
public void addDay(int day)
|
public void addDay(int day)
|
||||||
{
|
{
|
||||||
this.day.remove(ALL_VALUE);
|
this.day.remove(ALL_VALUE);
|
||||||
this.day.put(new Integer(day), value);
|
this.day.add(new Integer(day));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -566,7 +566,7 @@ public class CronJob {
|
||||||
public void setAllDays(boolean set)
|
public void setAllDays(boolean set)
|
||||||
{
|
{
|
||||||
if (set)
|
if (set)
|
||||||
this.day.put(ALL_VALUE, value);
|
this.day.add(ALL_VALUE);
|
||||||
else
|
else
|
||||||
this.day.remove(ALL_VALUE);
|
this.day.remove(ALL_VALUE);
|
||||||
}
|
}
|
||||||
|
@ -580,7 +580,7 @@ public class CronJob {
|
||||||
public void addWeekday(int weekday)
|
public void addWeekday(int weekday)
|
||||||
{
|
{
|
||||||
this.weekday.remove(ALL_VALUE);
|
this.weekday.remove(ALL_VALUE);
|
||||||
this.weekday.put(new Integer(weekday), value);
|
this.weekday.add(new Integer(weekday));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -604,7 +604,7 @@ public class CronJob {
|
||||||
public void setAllWeekdays(boolean set)
|
public void setAllWeekdays(boolean set)
|
||||||
{
|
{
|
||||||
if (set)
|
if (set)
|
||||||
this.weekday.put(ALL_VALUE, value);
|
this.weekday.add(ALL_VALUE);
|
||||||
else
|
else
|
||||||
this.weekday.remove(ALL_VALUE);
|
this.weekday.remove(ALL_VALUE);
|
||||||
}
|
}
|
||||||
|
@ -616,7 +616,7 @@ public class CronJob {
|
||||||
public void addHour(int hour)
|
public void addHour(int hour)
|
||||||
{
|
{
|
||||||
this.hour.remove(ALL_VALUE);
|
this.hour.remove(ALL_VALUE);
|
||||||
this.hour.put(new Integer(hour), value);
|
this.hour.add(new Integer(hour));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -638,7 +638,7 @@ public class CronJob {
|
||||||
public void setAllHours(boolean set)
|
public void setAllHours(boolean set)
|
||||||
{
|
{
|
||||||
if (set)
|
if (set)
|
||||||
this.hour.put(ALL_VALUE, value);
|
this.hour.add(ALL_VALUE);
|
||||||
else
|
else
|
||||||
this.hour.remove(ALL_VALUE);
|
this.hour.remove(ALL_VALUE);
|
||||||
}
|
}
|
||||||
|
@ -650,7 +650,7 @@ public class CronJob {
|
||||||
public void addMinute(int minute)
|
public void addMinute(int minute)
|
||||||
{
|
{
|
||||||
this.minute.remove(ALL_VALUE);
|
this.minute.remove(ALL_VALUE);
|
||||||
this.minute.put(new Integer(minute), value);
|
this.minute.add(new Integer(minute));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -672,12 +672,11 @@ public class CronJob {
|
||||||
public void setAllMinutes(boolean set)
|
public void setAllMinutes(boolean set)
|
||||||
{
|
{
|
||||||
if (set)
|
if (set)
|
||||||
this.minute.put(ALL_VALUE, value);
|
this.minute.add(ALL_VALUE);
|
||||||
else
|
else
|
||||||
this.minute.remove(ALL_VALUE);
|
this.minute.remove(ALL_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this entry's name
|
* Set this entry's name
|
||||||
*/
|
*/
|
||||||
|
@ -728,7 +727,8 @@ public class CronJob {
|
||||||
return this.timeout;
|
return this.timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString () {
|
public String toString ()
|
||||||
|
{
|
||||||
return "[CronJob " + name + "]";
|
return "[CronJob " + name + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -310,6 +310,7 @@ public final class HtmlEncoder {
|
||||||
swallowTwo.add("ul");
|
swallowTwo.add("ul");
|
||||||
|
|
||||||
/// to be treated as block level elements
|
/// to be treated as block level elements
|
||||||
|
swallowTwo.add("br");
|
||||||
swallowTwo.add("dd");
|
swallowTwo.add("dd");
|
||||||
swallowTwo.add("dt");
|
swallowTwo.add("dt");
|
||||||
swallowTwo.add("frameset");
|
swallowTwo.add("frameset");
|
||||||
|
@ -322,6 +323,26 @@ public final class HtmlEncoder {
|
||||||
swallowAll.add("tr");
|
swallowAll.add("tr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set of tags that are always empty
|
||||||
|
static final HashSet emptyTags = new HashSet();
|
||||||
|
|
||||||
|
static {
|
||||||
|
emptyTags.add("area");
|
||||||
|
emptyTags.add("base");
|
||||||
|
emptyTags.add("basefont");
|
||||||
|
emptyTags.add("br");
|
||||||
|
emptyTags.add("col");
|
||||||
|
emptyTags.add("frame");
|
||||||
|
emptyTags.add("hr");
|
||||||
|
emptyTags.add("img");
|
||||||
|
emptyTags.add("input");
|
||||||
|
emptyTags.add("isindex");
|
||||||
|
emptyTags.add("link");
|
||||||
|
emptyTags.add("meta");
|
||||||
|
emptyTags.add("param");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do "smart" encodging on a string. This means that valid HTML entities and tags,
|
* Do "smart" encodging on a string. This means that valid HTML entities and tags,
|
||||||
* Helma macros and HTML comments are passed through unescaped, while
|
* Helma macros and HTML comments are passed through unescaped, while
|
||||||
|
@ -470,11 +491,14 @@ public final class HtmlEncoder {
|
||||||
continue;
|
continue;
|
||||||
} else if (t > 1) {
|
} else if (t > 1) {
|
||||||
for (int k = 1; k < t; k++) {
|
for (int k = 1; k < t; k++) {
|
||||||
|
Object tag = openTags.pop();
|
||||||
|
if (!emptyTags.contains(tag)) {
|
||||||
ret.append("</");
|
ret.append("</");
|
||||||
ret.append(openTags.pop());
|
ret.append(tag);
|
||||||
ret.append(">");
|
ret.append(">");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
openTags.pop();
|
openTags.pop();
|
||||||
} else {
|
} else {
|
||||||
|
@ -496,7 +520,7 @@ public final class HtmlEncoder {
|
||||||
// if (i < l-2)
|
// if (i < l-2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((linebreaks > 0) && !Character.isWhitespace(c)) {
|
if ((linebreaks > 0 || swallowLinebreaks > 0) && !Character.isWhitespace(c)) {
|
||||||
if (!insidePreTag && (linebreaks > swallowLinebreaks)) {
|
if (!insidePreTag && (linebreaks > swallowLinebreaks)) {
|
||||||
linebreaks -= swallowLinebreaks;
|
linebreaks -= swallowLinebreaks;
|
||||||
|
|
||||||
|
@ -659,11 +683,14 @@ public final class HtmlEncoder {
|
||||||
|
|
||||||
if (o > 0) {
|
if (o > 0) {
|
||||||
for (int k = 0; k < o; k++) {
|
for (int k = 0; k < o; k++) {
|
||||||
|
Object tag = openTags.pop();
|
||||||
|
if (!emptyTags.contains(tag)) {
|
||||||
ret.append("</");
|
ret.append("</");
|
||||||
ret.append(openTags.pop());
|
ret.append(tag);
|
||||||
ret.append(">");
|
ret.append(">");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// add remaining newlines we may have collected
|
// add remaining newlines we may have collected
|
||||||
if ((linebreaks > 0) && (linebreaks > swallowLinebreaks)) {
|
if ((linebreaks > 0) && (linebreaks > swallowLinebreaks)) {
|
||||||
|
|
51
src/helma/util/StringUtils.java
Normal file
51
src/helma/util/StringUtils.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for String manipulation.
|
||||||
|
*/
|
||||||
|
public class StringUtils {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a string into an array of strings. Use comma and space
|
||||||
|
* as delimiters.
|
||||||
|
*/
|
||||||
|
public static String[] split(String str) {
|
||||||
|
return split(str, ", \t\n\r\f");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a string into an array of strings.
|
||||||
|
*/
|
||||||
|
public static String[] split(String str, String delim) {
|
||||||
|
if (str == null) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
StringTokenizer st = new StringTokenizer(str, delim);
|
||||||
|
String[] s = new String[st.countTokens()];
|
||||||
|
for (int i=0; i<s.length; i++) {
|
||||||
|
s[i] = st.nextToken();
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue