diff --git a/build/antclick/data/030SYS_INFO_sUSRPKeyInfo.koi b/build/antclick/data/030SYS_INFO_sUSRPKeyInfo.koi deleted file mode 100644 index 61ea401d..00000000 Binary files a/build/antclick/data/030SYS_INFO_sUSRPKeyInfo.koi and /dev/null differ diff --git a/build/antclick/data/037SYS_INFO_sUSRSchemaInfo.koi b/build/antclick/data/037SYS_INFO_sUSRSchemaInfo.koi deleted file mode 100644 index f947a15b..00000000 Binary files a/build/antclick/data/037SYS_INFO_sUSRSchemaInfo.koi and /dev/null differ diff --git a/build/antclick/data/042SYS_INFO_sUSRGrant.koi b/build/antclick/data/042SYS_INFO_sUSRGrant.koi deleted file mode 100644 index 6cda17f3..00000000 Binary files a/build/antclick/data/042SYS_INFO_sUSRGrant.koi and /dev/null differ diff --git a/build/antclick/data/051APP_AV_IMAGE.koi b/build/antclick/data/051APP_AV_IMAGE.koi deleted file mode 100644 index 93a65fea..00000000 Binary files a/build/antclick/data/051APP_AV_IMAGE.koi and /dev/null differ diff --git a/build/antclick/data/053APP_AV_POLL.koi b/build/antclick/data/053APP_AV_POLL.koi deleted file mode 100644 index 309a0697..00000000 Binary files a/build/antclick/data/053APP_AV_POLL.koi and /dev/null differ diff --git a/build/antclick/data/056APP_AV_TEXT.koi b/build/antclick/data/056APP_AV_TEXT.koi deleted file mode 100644 index f469ec8a..00000000 Binary files a/build/antclick/data/056APP_AV_TEXT.koi and /dev/null differ diff --git a/build/antclick/data/062SYS_INFO_sUSRSequenceInfo.koi b/build/antclick/data/062SYS_INFO_sUSRSequenceInfo.koi new file mode 100644 index 00000000..9a1b1043 Binary files /dev/null and b/build/antclick/data/062SYS_INFO_sUSRSequenceInfo.koi differ diff --git a/build/antclick/data/063SYS_INFO_sUSRSequence.koi b/build/antclick/data/063SYS_INFO_sUSRSequence.koi new file mode 100644 index 00000000..ea347872 Binary files /dev/null and b/build/antclick/data/063SYS_INFO_sUSRSequence.koi differ diff --git a/build/antclick/data/064SYS_INFO_sUSRPKeyInfo.koi b/build/antclick/data/064SYS_INFO_sUSRPKeyInfo.koi new file mode 100644 index 00000000..af698248 Binary files /dev/null and b/build/antclick/data/064SYS_INFO_sUSRPKeyInfo.koi differ diff --git a/build/antclick/data/031SYS_INFO_sUSRFKeyInfo.koi b/build/antclick/data/065SYS_INFO_sUSRFKeyInfo.koi similarity index 99% rename from build/antclick/data/031SYS_INFO_sUSRFKeyInfo.koi rename to build/antclick/data/065SYS_INFO_sUSRFKeyInfo.koi index 91fb5ab5..dad0818b 100644 Binary files a/build/antclick/data/031SYS_INFO_sUSRFKeyInfo.koi and b/build/antclick/data/065SYS_INFO_sUSRFKeyInfo.koi differ diff --git a/build/antclick/data/032SYS_INFO_sUSRUniqueInfo.koi b/build/antclick/data/066SYS_INFO_sUSRUniqueInfo.koi similarity index 98% rename from build/antclick/data/032SYS_INFO_sUSRUniqueInfo.koi rename to build/antclick/data/066SYS_INFO_sUSRUniqueInfo.koi index 34e01b90..b13eba42 100644 Binary files a/build/antclick/data/032SYS_INFO_sUSRUniqueInfo.koi and b/build/antclick/data/066SYS_INFO_sUSRUniqueInfo.koi differ diff --git a/build/antclick/data/033SYS_INFO_sUSRCheckInfo.koi b/build/antclick/data/067SYS_INFO_sUSRCheckInfo.koi similarity index 97% rename from build/antclick/data/033SYS_INFO_sUSRCheckInfo.koi rename to build/antclick/data/067SYS_INFO_sUSRCheckInfo.koi index 543a1732..0c523807 100644 Binary files a/build/antclick/data/033SYS_INFO_sUSRCheckInfo.koi and b/build/antclick/data/067SYS_INFO_sUSRCheckInfo.koi differ diff --git a/build/antclick/data/034SYS_INFO_sUSRPrimaryColumns.koi b/build/antclick/data/068SYS_INFO_sUSRPrimaryColumns.koi similarity index 72% rename from build/antclick/data/034SYS_INFO_sUSRPrimaryColumns.koi rename to build/antclick/data/068SYS_INFO_sUSRPrimaryColumns.koi index c9c3bc78..bad77217 100644 Binary files a/build/antclick/data/034SYS_INFO_sUSRPrimaryColumns.koi and b/build/antclick/data/068SYS_INFO_sUSRPrimaryColumns.koi differ diff --git a/build/antclick/data/035SYS_INFO_sUSRUniqueColumns.koi b/build/antclick/data/069SYS_INFO_sUSRUniqueColumns.koi similarity index 98% rename from build/antclick/data/035SYS_INFO_sUSRUniqueColumns.koi rename to build/antclick/data/069SYS_INFO_sUSRUniqueColumns.koi index f0fd85f4..8a10e176 100644 Binary files a/build/antclick/data/035SYS_INFO_sUSRUniqueColumns.koi and b/build/antclick/data/069SYS_INFO_sUSRUniqueColumns.koi differ diff --git a/build/antclick/data/036SYS_INFO_sUSRForeignColumns.koi b/build/antclick/data/070SYS_INFO_sUSRForeignColumns.koi similarity index 59% rename from build/antclick/data/036SYS_INFO_sUSRForeignColumns.koi rename to build/antclick/data/070SYS_INFO_sUSRForeignColumns.koi index 012a4fb4..a920b5ee 100644 Binary files a/build/antclick/data/036SYS_INFO_sUSRForeignColumns.koi and b/build/antclick/data/070SYS_INFO_sUSRForeignColumns.koi differ diff --git a/build/antclick/data/071SYS_INFO_sUSRSchemaInfo.koi b/build/antclick/data/071SYS_INFO_sUSRSchemaInfo.koi new file mode 100644 index 00000000..712b8277 Binary files /dev/null and b/build/antclick/data/071SYS_INFO_sUSRSchemaInfo.koi differ diff --git a/build/antclick/data/038SYS_INFO_sUSRDatabaseVars.koi b/build/antclick/data/072SYS_INFO_sUSRDatabaseVars.koi similarity index 89% rename from build/antclick/data/038SYS_INFO_sUSRDatabaseVars.koi rename to build/antclick/data/072SYS_INFO_sUSRDatabaseVars.koi index 4df76be6..95808129 100644 Binary files a/build/antclick/data/038SYS_INFO_sUSRDatabaseVars.koi and b/build/antclick/data/072SYS_INFO_sUSRDatabaseVars.koi differ diff --git a/build/antclick/data/039SYS_INFO_sUSRPassword.koi b/build/antclick/data/073SYS_INFO_sUSRPassword.koi similarity index 98% rename from build/antclick/data/039SYS_INFO_sUSRPassword.koi rename to build/antclick/data/073SYS_INFO_sUSRPassword.koi index e6913bf3..2b3b9bab 100644 Binary files a/build/antclick/data/039SYS_INFO_sUSRPassword.koi and b/build/antclick/data/073SYS_INFO_sUSRPassword.koi differ diff --git a/build/antclick/data/040SYS_INFO_sUSRUserPriv.koi b/build/antclick/data/074SYS_INFO_sUSRUserPriv.koi similarity index 98% rename from build/antclick/data/040SYS_INFO_sUSRUserPriv.koi rename to build/antclick/data/074SYS_INFO_sUSRUserPriv.koi index c7070647..e2eb5d27 100644 Binary files a/build/antclick/data/040SYS_INFO_sUSRUserPriv.koi and b/build/antclick/data/074SYS_INFO_sUSRUserPriv.koi differ diff --git a/build/antclick/data/041SYS_INFO_sUSRUserConnectPriv.koi b/build/antclick/data/075SYS_INFO_sUSRUserConnectPriv.koi similarity index 91% rename from build/antclick/data/041SYS_INFO_sUSRUserConnectPriv.koi rename to build/antclick/data/075SYS_INFO_sUSRUserConnectPriv.koi index 7db5798c..ec993bd6 100644 Binary files a/build/antclick/data/041SYS_INFO_sUSRUserConnectPriv.koi and b/build/antclick/data/075SYS_INFO_sUSRUserConnectPriv.koi differ diff --git a/build/antclick/data/076SYS_INFO_sUSRGrant.koi b/build/antclick/data/076SYS_INFO_sUSRGrant.koi new file mode 100644 index 00000000..64d56c2b Binary files /dev/null and b/build/antclick/data/076SYS_INFO_sUSRGrant.koi differ diff --git a/build/antclick/data/043SYS_INFO_sUSRService.koi b/build/antclick/data/077SYS_INFO_sUSRService.koi similarity index 91% rename from build/antclick/data/043SYS_INFO_sUSRService.koi rename to build/antclick/data/077SYS_INFO_sUSRService.koi index c0b2d938..402acdcd 100644 Binary files a/build/antclick/data/043SYS_INFO_sUSRService.koi and b/build/antclick/data/077SYS_INFO_sUSRService.koi differ diff --git a/build/antclick/data/044SYS_INFO_sUSRFunctionFactory.koi b/build/antclick/data/078SYS_INFO_sUSRFunctionFactory.koi similarity index 91% rename from build/antclick/data/044SYS_INFO_sUSRFunctionFactory.koi rename to build/antclick/data/078SYS_INFO_sUSRFunctionFactory.koi index 217a1457..74ef4243 100644 Binary files a/build/antclick/data/044SYS_INFO_sUSRFunctionFactory.koi and b/build/antclick/data/078SYS_INFO_sUSRFunctionFactory.koi differ diff --git a/build/antclick/data/079SYS_INFO_sUSRFunction.koi b/build/antclick/data/079SYS_INFO_sUSRFunction.koi new file mode 100644 index 00000000..b5843af6 Binary files /dev/null and b/build/antclick/data/079SYS_INFO_sUSRFunction.koi differ diff --git a/build/antclick/data/046SYS_INFO_sUSRView.koi b/build/antclick/data/080SYS_INFO_sUSRView.koi similarity index 95% rename from build/antclick/data/046SYS_INFO_sUSRView.koi rename to build/antclick/data/080SYS_INFO_sUSRView.koi index 46407714..de4fad16 100644 Binary files a/build/antclick/data/046SYS_INFO_sUSRView.koi and b/build/antclick/data/080SYS_INFO_sUSRView.koi differ diff --git a/build/antclick/data/047SYS_INFO_sUSRLabel.koi b/build/antclick/data/081SYS_INFO_sUSRLabel.koi similarity index 84% rename from build/antclick/data/047SYS_INFO_sUSRLabel.koi rename to build/antclick/data/081SYS_INFO_sUSRLabel.koi index 6eaa3a7b..05a0bf1a 100644 Binary files a/build/antclick/data/047SYS_INFO_sUSRLabel.koi and b/build/antclick/data/081SYS_INFO_sUSRLabel.koi differ diff --git a/build/antclick/data/082SYS_INFO_sUSRDataTrigger.koi b/build/antclick/data/082SYS_INFO_sUSRDataTrigger.koi new file mode 100644 index 00000000..a472e899 Binary files /dev/null and b/build/antclick/data/082SYS_INFO_sUSRDataTrigger.koi differ diff --git a/build/antclick/data/048APP_AV_ACCESSLOG.koi b/build/antclick/data/083APP_AV_ACCESSLOG.koi similarity index 61% rename from build/antclick/data/048APP_AV_ACCESSLOG.koi rename to build/antclick/data/083APP_AV_ACCESSLOG.koi index 4cd25df2..ef2833f9 100644 Binary files a/build/antclick/data/048APP_AV_ACCESSLOG.koi and b/build/antclick/data/083APP_AV_ACCESSLOG.koi differ diff --git a/build/antclick/data/049APP_AV_CHOICE.koi b/build/antclick/data/084APP_AV_CHOICE.koi similarity index 66% rename from build/antclick/data/049APP_AV_CHOICE.koi rename to build/antclick/data/084APP_AV_CHOICE.koi index b9951a30..f0c2021f 100644 Binary files a/build/antclick/data/049APP_AV_CHOICE.koi and b/build/antclick/data/084APP_AV_CHOICE.koi differ diff --git a/build/antclick/data/050APP_AV_FILE.koi b/build/antclick/data/085APP_AV_FILE.koi similarity index 50% rename from build/antclick/data/050APP_AV_FILE.koi rename to build/antclick/data/085APP_AV_FILE.koi index 05a0ae93..9e02d65c 100644 Binary files a/build/antclick/data/050APP_AV_FILE.koi and b/build/antclick/data/085APP_AV_FILE.koi differ diff --git a/build/antclick/data/086APP_AV_IMAGE.koi b/build/antclick/data/086APP_AV_IMAGE.koi new file mode 100644 index 00000000..330cbc42 Binary files /dev/null and b/build/antclick/data/086APP_AV_IMAGE.koi differ diff --git a/build/antclick/data/052APP_AV_MEMBERSHIP.koi b/build/antclick/data/087APP_AV_MEMBERSHIP.koi similarity index 57% rename from build/antclick/data/052APP_AV_MEMBERSHIP.koi rename to build/antclick/data/087APP_AV_MEMBERSHIP.koi index 86ddd807..92e22200 100644 Binary files a/build/antclick/data/052APP_AV_MEMBERSHIP.koi and b/build/antclick/data/087APP_AV_MEMBERSHIP.koi differ diff --git a/build/antclick/data/088APP_AV_POLL.koi b/build/antclick/data/088APP_AV_POLL.koi new file mode 100644 index 00000000..1beee814 Binary files /dev/null and b/build/antclick/data/088APP_AV_POLL.koi differ diff --git a/build/antclick/data/054APP_AV_SKIN.koi b/build/antclick/data/089APP_AV_SKIN.koi similarity index 54% rename from build/antclick/data/054APP_AV_SKIN.koi rename to build/antclick/data/089APP_AV_SKIN.koi index 6625eb8c..5cf0a812 100644 Binary files a/build/antclick/data/054APP_AV_SKIN.koi and b/build/antclick/data/089APP_AV_SKIN.koi differ diff --git a/build/antclick/data/055APP_AV_SYSLOG.koi b/build/antclick/data/090APP_AV_SYSLOG.koi similarity index 63% rename from build/antclick/data/055APP_AV_SYSLOG.koi rename to build/antclick/data/090APP_AV_SYSLOG.koi index 38136209..cee072b3 100644 Binary files a/build/antclick/data/055APP_AV_SYSLOG.koi and b/build/antclick/data/090APP_AV_SYSLOG.koi differ diff --git a/build/antclick/data/058APP_AV_VOTE.koi b/build/antclick/data/092APP_AV_VOTE.koi similarity index 56% rename from build/antclick/data/058APP_AV_VOTE.koi rename to build/antclick/data/092APP_AV_VOTE.koi index bd032dca..9e06436b 100644 Binary files a/build/antclick/data/058APP_AV_VOTE.koi and b/build/antclick/data/092APP_AV_VOTE.koi differ diff --git a/build/antclick/data/059APP_AV_SITE.koi b/build/antclick/data/093APP_AV_SITE.koi similarity index 54% rename from build/antclick/data/059APP_AV_SITE.koi rename to build/antclick/data/093APP_AV_SITE.koi index c7dcc6b1..dd5059ee 100644 Binary files a/build/antclick/data/059APP_AV_SITE.koi and b/build/antclick/data/093APP_AV_SITE.koi differ diff --git a/build/antclick/data/060APP_AV_USER.koi b/build/antclick/data/094APP_AV_USER.koi similarity index 51% rename from build/antclick/data/060APP_AV_USER.koi rename to build/antclick/data/094APP_AV_USER.koi index 7341ba53..7be3f430 100644 Binary files a/build/antclick/data/060APP_AV_USER.koi and b/build/antclick/data/094APP_AV_USER.koi differ diff --git a/build/antclick/data/061APP_AV_SHORTCUT.koi b/build/antclick/data/095APP_AV_SHORTCUT.koi similarity index 61% rename from build/antclick/data/061APP_AV_SHORTCUT.koi rename to build/antclick/data/095APP_AV_SHORTCUT.koi index 45f2cf57..adf0b128 100644 Binary files a/build/antclick/data/061APP_AV_SHORTCUT.koi and b/build/antclick/data/095APP_AV_SHORTCUT.koi differ diff --git a/build/antclick/data/096APP_AV_TEXT.koi b/build/antclick/data/096APP_AV_TEXT.koi new file mode 100644 index 00000000..53a6ba67 Binary files /dev/null and b/build/antclick/data/096APP_AV_TEXT.koi differ diff --git a/build/antclick/data/045SYS_INFO_sUSRFunction.koi b/build/antclick/data/BlobStore.koi similarity index 53% rename from build/antclick/data/045SYS_INFO_sUSRFunction.koi rename to build/antclick/data/BlobStore.koi index 1c3c4c6c..b86b02f8 100644 Binary files a/build/antclick/data/045SYS_INFO_sUSRFunction.koi and b/build/antclick/data/BlobStore.koi differ diff --git a/build/antclick/data/DefaultDatabase.sf b/build/antclick/data/DefaultDatabase.sf deleted file mode 100644 index 4ad38cdc..00000000 Binary files a/build/antclick/data/DefaultDatabase.sf and /dev/null differ diff --git a/build/antclick/data/DefaultDatabase_sf.koi b/build/antclick/data/DefaultDatabase_sf.koi new file mode 100644 index 00000000..c02904d4 Binary files /dev/null and b/build/antclick/data/DefaultDatabase_sf.koi differ diff --git a/build/antclick/lib/ext/mckoidb.jar b/build/antclick/lib/ext/mckoidb.jar index f86e3daa..309edf66 100644 Binary files a/build/antclick/lib/ext/mckoidb.jar and b/build/antclick/lib/ext/mckoidb.jar differ diff --git a/build/build.xml b/build/build.xml index cd0dd648..ee9717ba 100644 --- a/build/build.xml +++ b/build/build.xml @@ -8,7 +8,7 @@ - + @@ -28,8 +28,8 @@ - - + + diff --git a/lib/ext/mysql.jar b/lib/ext/mysql.jar deleted file mode 100644 index 32f04104..00000000 Binary files a/lib/ext/mysql.jar and /dev/null differ diff --git a/src/helma/doc/DocApplication.java b/src/helma/doc/DocApplication.java index be1bbb38..c8e8c4de 100644 --- a/src/helma/doc/DocApplication.java +++ b/src/helma/doc/DocApplication.java @@ -1,98 +1,197 @@ +/* + * 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.doc; import helma.framework.IPathElement; import helma.main.Server; +import helma.util.SystemProperties; import java.io.*; import java.util.*; -public class DocApplication extends DocDirElement { +/** + * + */ +public class DocApplication extends DocDirElement { + HashSet excluded; - public static void main (String args[]) { -// DocApplication app; -// app = new DocApplication (args[0], args[1]); -// app.readApplication (); + /** + * Creates a new DocApplication object. + * + * @param name ... + * @param location ... + * + * @throws DocException ... + */ + public DocApplication(String name, File location) throws DocException { + super(name, location, APPLICATION); + readProps(); + } -// DocPrototype el = DocPrototype.newInstance (new File(args[0])); -// el.readFiles (); + /** + * Creates a new DocApplication object. + * + * @param name ... + * @param appDir ... + * + * @throws DocException ... + */ + public DocApplication(String name, String appDir) throws DocException { + super(name, new File(appDir), APPLICATION); + readProps(); + } -// DocFunction func = DocFunction.newTemplate (new File(args[0])); -// DocFunction func = DocFunction.newAction (new File(args[0])); + /** + * + * + * @param args ... + */ + public static void main(String[] args) { + // DocApplication app; + // app = new DocApplication (args[0], args[1]); + // app.readApplication (); + // DocPrototype el = DocPrototype.newInstance (new File(args[0])); + // el.readFiles (); + // DocFunction func = DocFunction.newTemplate (new File(args[0])); + // DocFunction func = DocFunction.newAction (new File(args[0])); + DocFunction[] func = DocFunction.newFunctions(new File(args[0])); - DocFunction[] func = DocFunction.newFunctions (new File (args[0])); + // DocSkin skin = DocSkin.newInstance (new File (args[0])); + // System.out.println (func.getContent ()); + // System.out.println ("\n\n\ncomment = " + func.getComment ()); + } -// DocSkin skin = DocSkin.newInstance (new File (args[0])); -// System.out.println (func.getContent ()); -// System.out.println ("\n\n\ncomment = " + func.getComment ()); - } + /** + * reads the app.properties file and parses for helma.excludeDocs + */ + private void readProps() { + File propsFile = new File(location, "app.properties"); + SystemProperties serverProps = Server.getServer().getProperties(); + SystemProperties appProps = new SystemProperties(propsFile.getAbsolutePath(), + serverProps); + excluded = new HashSet(); + addExclude("cvs"); + addExclude(".docs"); - public DocApplication (String name, File location) throws DocException { - super (name, location, APPLICATION); - } + String excludeProps = appProps.getProperty("helma.excludeDocs"); - public DocApplication (String name, String appDir) throws DocException { - super (name, new File (appDir), APPLICATION); - } + if (excludeProps != null) { + StringTokenizer tok = new StringTokenizer(excludeProps, ","); + while (tok.hasMoreTokens()) { + String str = tok.nextToken().trim(); - /** - * reads all prototypes and files of the application - */ - public void readApplication () { - String arr[] = location.list (); - children.clear (); - for (int i=0; i e2.getType())) { + return 1; + } else if ((mode == BY_TYPE) && (e1.getType() < e2.getType())) { + return -1; + } else { + return e1.name.compareTo(e2.name); + } + } - public int compare(Object obj1, Object obj2) { - DocElement e1 = (DocElement)obj1; - DocElement e2 = (DocElement)obj2; - if (mode==BY_TYPE && e1.getType()>e2.getType()) - return 1; - else if (mode==BY_TYPE && e1.getType()beginLine && ct beginLine) && (ct < endLine)) { + buf.append(line + "\n"); + } else if (ct == endLine) { + buf.append(line.substring(0, endColumn)); + } + } + } catch (Exception e) { + debug(e.getMessage()); + } + + content = buf.toString(); + } + + /** + * connects all available specialTokens starting at the given token. + */ + protected void parseCommentFromToken(Token tok) { + StringBuffer buf = new StringBuffer(); + + while (tok.specialToken != null) { + buf.append(tok.specialToken.toString()); + tok = tok.specialToken; + } + + parseComment(buf.toString().trim()); + } } - diff --git a/src/helma/doc/DocFunction.java b/src/helma/doc/DocFunction.java index fb18c3e5..577d5c2c 100644 --- a/src/helma/doc/DocFunction.java +++ b/src/helma/doc/DocFunction.java @@ -1,154 +1,185 @@ -package helma.doc; +/* + * 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$ + */ -import java.io.*; -import java.util.*; +package helma.doc; import FESI.Parser.*; import helma.framework.IPathElement; +import java.io.*; +import java.util.*; +/** + * + */ public class DocFunction extends DocFileElement { + protected DocFunction(String name, File location, DocElement parent, int type) { + super(name, location, type); + this.parent = parent; + } - /** - * creates a new independent DocFunction object of type ACTION - */ - public static DocFunction newAction (File location) { - return newAction (location, null); - } + /** + * creates a new independent DocFunction object of type ACTION + */ + public static DocFunction newAction(File location) { + return newAction(location, null); + } + /** + * creates a new DocFunction object of type ACTION connected to another DocElement + */ + public static DocFunction newAction(File location, DocElement parent) { + String name = nameFromFile(location, ".hac"); + DocFunction func = new DocFunction(name, location, parent, ACTION); - /** - * creates a new DocFunction object of type ACTION connected to another DocElement - */ - public static DocFunction newAction (File location, DocElement parent) { - String name = nameFromFile (location, ".hac"); - DocFunction func = new DocFunction (name, location, parent, ACTION); - func.parseActionFile (); - return func; - } + func.parseActionFile(); - /** - * creates a new independent DocFunction object of type TEMPLATE - */ - public static DocFunction newTemplate (File location) { - return newTemplate (location, null); - } + return func; + } + /** + * creates a new independent DocFunction object of type TEMPLATE + */ + public static DocFunction newTemplate(File location) { + return newTemplate(location, null); + } - /** - * creates a new DocFunction object of type TEMPLATE connected to another DocElement - */ - public static DocFunction newTemplate (File location, DocElement parent) { - String name = nameFromFile (location, ".hsp"); - DocFunction func = new DocFunction (name, location, parent, TEMPLATE); - func.parseTemplateFile (); - return func; - } + /** + * creates a new DocFunction object of type TEMPLATE connected to another DocElement + */ + public static DocFunction newTemplate(File location, DocElement parent) { + String name = nameFromFile(location, ".hsp"); + DocFunction func = new DocFunction(name, location, parent, TEMPLATE); + func.parseTemplateFile(); - /** - * reads a function file and creates independent DocFunction objects of type FUNCTION - */ - public static DocFunction[] newFunctions (File location) { - return newFunctions (location, null); - } + return func; + } + /** + * reads a function file and creates independent DocFunction objects of type FUNCTION + */ + public static DocFunction[] newFunctions(File location) { + return newFunctions(location, null); + } - /** - * reads a function file and creates DocFunction objects of type FUNCTION - * connected to another DocElement. - */ - public static DocFunction[] newFunctions (File location, DocElement parent) { - Vector vec = new Vector (); - EcmaScriptTokenManager mgr = createTokenManager(location); - Token tok = mgr.getNextToken(); - while (tok.kind!=0) { - if (tok.kind == EcmaScriptConstants.FUNCTION) { - // store the start position of the function: - int beginLine = tok.beginLine; - int beginColumn = tok.beginColumn; - // the name is stored in the next token: - String funcName = mgr.getNextToken().toString(); - // create the function object - DocFunction func; - if (funcName.endsWith("_action")) - func = new DocFunction (funcName, location, parent, ACTION); - else if (funcName.endsWith("_macro")) - func = new DocFunction (funcName, location, parent, MACRO); - else - func = new DocFunction (funcName, location, parent, FUNCTION); - // parse the comment from the special token(s) before this token: - func.parseCommentFromToken (tok); - // find the parameters of this function, but only if it's - // neither macro nor action: - if (func.type==FUNCTION) { - while (tok.kind!=0 && tok.kind!=EcmaScriptConstants.RPAREN) { - if (tok.kind==EcmaScriptConstants.IDENTIFIER) { - func.addParameter (tok.image); - } - tok = mgr.getNextToken (); - } - } else { - tok = mgr.getNextToken (); - } - // now find the end of the function: - int endLine=0, endColumn=0; - while (tok.kind!=0 && tok.kind!=EcmaScriptConstants.FUNCTION) { - endLine = tok.endLine; - endColumn = tok.endColumn; - tok = mgr.getNextToken (); - } - // now we know the exact position of the function in the file, - // re-read it and extract the source code: - func.parseSource (location, beginLine, beginColumn, endLine, endColumn); - vec.add (func); - } - if (tok.kind != EcmaScriptConstants.FUNCTION) { - tok = mgr.getNextToken(); - } - } - return (DocFunction[]) vec.toArray (new DocFunction[0]); - } + /** + * reads a function file and creates DocFunction objects of type FUNCTION + * connected to another DocElement. + */ + public static DocFunction[] newFunctions(File location, DocElement parent) { + Vector vec = new Vector(); + EcmaScriptTokenManager mgr = createTokenManager(location); + Token tok = mgr.getNextToken(); - protected DocFunction (String name, File location, DocElement parent, int type) { - super (name, location, type); - this.parent = parent; - } + while (tok.kind != 0) { + if (tok.kind == EcmaScriptConstants.FUNCTION) { + // store the start position of the function: + int beginLine = tok.beginLine; + int beginColumn = tok.beginColumn; + // the name is stored in the next token: + String funcName = mgr.getNextToken().toString(); - /** - * reads the content of a .hac file and parses the comment before the first - * javascript element - */ - private void parseActionFile () { - EcmaScriptTokenManager mgr = createTokenManager(location); - Token tok = mgr.getNextToken(); - parseCommentFromToken (tok); - content = readFile (location); - } + // create the function object + DocFunction func; + if (funcName.endsWith("_action")) { + func = new DocFunction(funcName, location, parent, ACTION); + } else if (funcName.endsWith("_macro")) { + func = new DocFunction(funcName, location, parent, MACRO); + } else { + func = new DocFunction(funcName, location, parent, FUNCTION); + } - /** - * reads the content of a .hsp file and parses the comment before the first - * javascript element (only if file starts with >%-tag!). - */ - private void parseTemplateFile () { - content = readFile (location); - StringReader str = new StringReader (content.substring (content.indexOf("<%") + 2, content.indexOf ("%>"))); - ASCII_CharStream ascii = new ASCII_CharStream (str,1,1); - EcmaScriptTokenManager mgr = new EcmaScriptTokenManager (ascii); - Token tok = mgr.getNextToken(); - parseCommentFromToken (tok); - } + // parse the comment from the special token(s) before this token: + func.parseCommentFromToken(tok); + // find the parameters of this function, but only if it's + // neither macro nor action: + if (func.type == FUNCTION) { + while ((tok.kind != 0) && (tok.kind != EcmaScriptConstants.RPAREN)) { + if (tok.kind == EcmaScriptConstants.IDENTIFIER) { + func.addParameter(tok.image); + } - /** - * from helma.framework.IPathElement. All macros, templates, actions etc - * have the same prototype. - */ - public java.lang.String getPrototype () { - return "docfunction"; - } + tok = mgr.getNextToken(); + } + } else { + tok = mgr.getNextToken(); + } + // now find the end of the function: + int endLine = 0; + // now find the end of the function: + int endColumn = 0; + + while ((tok.kind != 0) && (tok.kind != EcmaScriptConstants.FUNCTION)) { + endLine = tok.endLine; + endColumn = tok.endColumn; + tok = mgr.getNextToken(); + } + + // now we know the exact position of the function in the file, + // re-read it and extract the source code: + func.parseSource(location, beginLine, beginColumn, endLine, endColumn); + vec.add(func); + } + + if (tok.kind != EcmaScriptConstants.FUNCTION) { + tok = mgr.getNextToken(); + } + } + + return (DocFunction[]) vec.toArray(new DocFunction[0]); + } + + /** + * reads the content of a .hac file and parses the comment before the first + * javascript element + */ + private void parseActionFile() { + EcmaScriptTokenManager mgr = createTokenManager(location); + Token tok = mgr.getNextToken(); + + parseCommentFromToken(tok); + content = readFile(location); + } + + /** + * reads the content of a .hsp file and parses the comment before the first + * javascript element (only if file starts with >%-tag!). + */ + private void parseTemplateFile() { + content = readFile(location); + + StringReader str = new StringReader(content.substring(content.indexOf("<%") + 2, + content.indexOf("%>"))); + ASCII_CharStream ascii = new ASCII_CharStream(str, 1, 1); + EcmaScriptTokenManager mgr = new EcmaScriptTokenManager(ascii); + Token tok = mgr.getNextToken(); + + parseCommentFromToken(tok); + } + + /** + * from helma.framework.IPathElement. All macros, templates, actions etc + * have the same prototype. + */ + public java.lang.String getPrototype() { + return "docfunction"; + } } - diff --git a/src/helma/doc/DocProperties.java b/src/helma/doc/DocProperties.java index 5eb640ab..6a050859 100644 --- a/src/helma/doc/DocProperties.java +++ b/src/helma/doc/DocProperties.java @@ -1,3 +1,19 @@ +/* + * 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.doc; import helma.framework.IPathElement; @@ -5,59 +21,76 @@ import helma.util.SystemProperties; import java.io.*; import java.util.*; -public class DocProperties extends DocFileElement { +/** + * + */ +public class DocProperties extends DocFileElement { + Properties props = null; - Properties props = null; + protected DocProperties(File location, DocElement parent) + throws DocException { + super(location.getName(), location, PROPERTIES); + this.parent = parent; + content = readFile(location); + props = new SystemProperties(); - /** - * creates a new independent DocProperties object - */ - public static DocProperties newInstance (File location) { - return newInstance (location, null); - } + try { + props.load(new FileInputStream(location)); + } catch (IOException e) { + debug("couldn't read file: " + e.toString()); + } catch (Exception e) { + throw new DocException(e.toString()); + } + } - /** - * creates a new DocProperties object connected to another DocElement - */ - public static DocProperties newInstance (File location, DocElement parent) { - try { - return new DocProperties (location, parent); - } catch (DocException doc) { - return null; - } - } + /** + * creates a new independent DocProperties object + */ + public static DocProperties newInstance(File location) { + return newInstance(location, null); + } - protected DocProperties (File location, DocElement parent) throws DocException { - super (location.getName (), location, PROPERTIES); - this.parent = parent; - content = readFile (location); - props = new SystemProperties (); - try { - props.load (new FileInputStream (location)); - } catch (IOException e) { - debug ("couldn't read file: " + e.toString ()); - } catch (Exception e) { - throw new DocException (e.toString ()); - } - } + /** + * creates a new DocProperties object connected to another DocElement + */ + public static DocProperties newInstance(File location, DocElement parent) { + try { + return new DocProperties(location, parent); + } catch (DocException doc) { + return null; + } + } - public Properties getProperties () { - return props; - } + /** + * + * + * @return ... + */ + public Properties getProperties() { + return props; + } - public Properties getMappings () { - Properties childProps = new Properties (); - for (Enumeration e = props.keys (); e.hasMoreElements (); ) { - String key = (String) e.nextElement (); - String value = props.getProperty (key); - if (value.startsWith ("collection") || value.startsWith ("object") || value.startsWith ("mountpoint")) { - String prototype = value.substring (value.indexOf("(")+1, value.indexOf(")")).trim (); - childProps.setProperty (key, prototype); - } - - } - return childProps; - } + /** + * + * + * @return ... + */ + public Properties getMappings() { + Properties childProps = new Properties(); + for (Enumeration e = props.keys(); e.hasMoreElements();) { + String key = (String) e.nextElement(); + String value = props.getProperty(key); + + if (value.startsWith("collection") || value.startsWith("object") || + value.startsWith("mountpoint")) { + String prototype = value.substring(value.indexOf("(") + 1, + value.indexOf(")")).trim(); + + childProps.setProperty(key, prototype); + } + } + + return childProps; + } } - diff --git a/src/helma/doc/DocPrototype.java b/src/helma/doc/DocPrototype.java index 45dc9e86..28f390be 100644 --- a/src/helma/doc/DocPrototype.java +++ b/src/helma/doc/DocPrototype.java @@ -1,110 +1,150 @@ +/* + * 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.doc; +import FESI.Parser.*; import helma.framework.IPathElement; import java.io.*; import java.util.*; -import FESI.Parser.*; +/** + * + */ +public class DocPrototype extends DocDirElement { + private DocProperties typeProperties = null; + private DocPrototype parentPrototype = null; -public class DocPrototype extends DocDirElement { + private DocPrototype(String name, File location, DocElement parent) { + super(name, location, PROTOTYPE); + this.parent = parent; + typeProperties = DocProperties.newInstance(new File(location, "type.properties")); + } - private DocProperties typeProperties = null; - private DocPrototype parentPrototype = null; + /** + * creates a prototype that is independent of an + * application object + * @param homedirectory + */ + public static DocPrototype newInstance(File location) { + return newInstance(location, null); + } - /** - * creates a prototype that is independent of an - * application object - * @param homedirectory - */ - public static DocPrototype newInstance (File location) { - return newInstance (location, null); - } + /** + * creates a prototype that is connected to an + * application object and resides in app's home dir. + * @param application + * @param name + */ + public static DocPrototype newInstance(File location, DocElement parent) { + DocPrototype pt = new DocPrototype(location.getName(), location, parent); - /** - * creates a prototype that is connected to an - * application object and resides in app's home dir. - * @param application - * @param name - */ - public static DocPrototype newInstance (File location, DocElement parent) { - DocPrototype pt = new DocPrototype (location.getName (), location, parent); - return pt; - } + return pt; + } - private DocPrototype (String name, File location, DocElement parent) { - super (name, location, PROTOTYPE); - this.parent = parent; - typeProperties = DocProperties.newInstance (new File (location, "type.properties")); - } + /** + * checks the type.properites for _extend values and connected a possible + * parent prototype with this prototype. this can't be successfull at construction + * time but only -after- all prototypes are parsed and attached to a parent + * DocApplication object. + */ + public void checkInheritance() { + // hopobject is the top prototype: + if (name.equals("hopobject")) { + return; + } + if (typeProperties != null) { + // check for "_extends" in the the type.properties + String ext = typeProperties.getProperties().getProperty("_extends"); - /** - * checks the type.properites for _extend values and connected a possible - * parent prototype with this prototype. this can't be successfull at construction - * time but only -after- all prototypes are parsed and attached to a parent - * DocApplication object. - */ - public void checkInheritance () { - // hopobject is the top prototype: - if (name.equals("hopobject")) - return; - if (typeProperties!=null) { - // check for "_extends" in the the type.properties - String ext = typeProperties.getProperties ().getProperty ("_extends"); - if (ext!=null && parent!=null) { - // try to get the prototype if available - parentPrototype = (DocPrototype) parent.getChildElement ("prototype_" + ext); - } - } - if (parentPrototype==null && parent!=null && !name.equals("global")) { - // if no _extend was set, get the hopobject prototype - parentPrototype = (DocPrototype) parent.getChildElement ("prototype_hopobject"); - } - } + if ((ext != null) && (parent != null)) { + // try to get the prototype if available + parentPrototype = (DocPrototype) parent.getChildElement("prototype_" + + ext); + } + } - public DocPrototype getParentPrototype () { - return parentPrototype; - } + if ((parentPrototype == null) && (parent != null) && !name.equals("global")) { + // if no _extend was set, get the hopobject prototype + parentPrototype = (DocPrototype) parent.getChildElement("prototype_hopobject"); + } + } + /** + * + * + * @return ... + */ + public DocPrototype getParentPrototype() { + return parentPrototype; + } - public DocProperties getTypeProperties () { - return typeProperties; - } + /** + * + * + * @return ... + */ + public DocProperties getTypeProperties() { + return typeProperties; + } + /** + * runs through the prototype directory and parses all helma files + */ + public void readFiles() { + children.clear(); - /** - * runs through the prototype directory and parses all helma files - */ - public void readFiles () { - children.clear (); - String arr[] = location.list (); - for (int i=0; i i+2) { - String str = (new String (source, i+2, j-i)).trim (); - if (str.endsWith("%>")) - str = str.substring (0, str.length()-2); - if (str.indexOf (" ")>-1) - str = str.substring (0, str.indexOf(" ")); - if (str.indexOf(".")>-1 && - (str.startsWith ("param.") - || str.startsWith ("response.") - || str.startsWith("request.") - || str.startsWith ("session.") - ) && !partBuffer.contains(str)) { - partBuffer.add (str); - } - start = j+2; - } - i = j+1; - } - } - String[] strArr = (String[]) partBuffer.toArray (new String [0]); - Arrays.sort (strArr); - for (int i=0; i (i + 2)) { + String str = (new String(source, i + 2, j - i)).trim(); + if (str.endsWith("%>")) { + str = str.substring(0, str.length() - 2); + } + + if (str.startsWith("//")) { + parseComment(str); + } else { + if (str.indexOf(" ") > -1) { + str = str.substring(0, str.indexOf(" ")); + } + + if ((str.indexOf(".") > -1) && + (str.startsWith("param.") || str.startsWith("response.") || + str.startsWith("request.") || str.startsWith("session.")) && + !partBuffer.contains(str)) { + partBuffer.add(str); + } + } + + start = j + 2; + } + + i = j + 1; + } + } + + String[] strArr = (String[]) partBuffer.toArray(new String[0]); + + Arrays.sort(strArr); + + for (int i = 0; i < strArr.length; i++) { + addParameter(strArr[i]); + } + } + + /** + * from helma.framework.IPathElement. Use the same prototype as functions etc. + */ + public java.lang.String getPrototype() { + return "docfunction"; + } } - diff --git a/src/helma/doc/DocTag.java b/src/helma/doc/DocTag.java index 690500ce..f713fb86 100644 --- a/src/helma/doc/DocTag.java +++ b/src/helma/doc/DocTag.java @@ -1,108 +1,176 @@ +/* + * 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.doc; import java.util.*; -public final class DocTag { +/** + * + */ +public final class DocTag { + // for public use we have less types than + // internally. eg, we're combining "return" and "returns" + // or "arg" and "param". + // the values here have to match the index of + // the tags-array! + public static final int PARAMETER = 0; + public static final int RETURN = 2; + public static final int AUTHOR = 4; + public static final int VERSION = 5; + public static final int SEE = 6; + public static final int DEPRECATED = 7; + public static final int OVERRIDES = 8; + public static final String[][] tags = { + { "@arg", "Argument" }, + { "@param", "Parameter" }, + { "@return", "Returns" }, + { "@returns", "Returns" }, + { "@author", "Author" }, + { "@version", "Version" }, + { "@see", "See also" }, + { "@deprecated", "Deprecated" }, + { "@overrides", "Overrides" } + }; + private String name; - // for public use we have less types than - // internally. eg, we're combining "return" and "returns" - // or "arg" and "param". - // the values here have to match the index of - // the tags-array! - public static final int PARAMETER = 0; - public static final int RETURN = 2; - public static final int AUTHOR = 4; - public static final int VERSION = 5; - public static final int SEE = 6; - public static final int DEPRECATED = 7; - public static final int OVERRIDES = 8; + // "kind" is for internal use, "type" is external + private int kind; + private String text; - public static final String[][] tags = { - {"@arg","Argument"}, - {"@param","Parameter"}, - {"@return","Returns"}, - {"@returns","Returns"}, - {"@author","Author"}, - {"@version","Version"}, - {"@see","See also"}, - {"@deprecated", "Deprecated"}, - {"@overrides", "Overrides"} - }; + private DocTag(int kind, String name, String text) { + this.kind = kind; + this.name = (name != null) ? name : ""; + this.text = (text != null) ? text : ""; + } - private String name; - // "kind" is for internal use, "type" is external - private int kind; - private String text; + /** + * + * + * @param rawTag ... + * + * @return ... + */ + public static boolean isTagStart(String rawTag) { + if (getTagNumber(rawTag) > -1) { + return true; + } else { + return false; + } + } - public static boolean isTagStart (String rawTag) { - if (getTagNumber(rawTag) > -1) - return true; - else - return false; - } + /** + * + * + * @param rawTag ... + * + * @return ... + * + * @throws DocException ... + */ + public static DocTag parse(String rawTag) throws DocException { + int kind = getTagNumber(rawTag); - public static DocTag parse (String rawTag) throws DocException { - int kind = getTagNumber (rawTag); - if (kind == -1) - throw new DocException ("unsupported tag type: " + rawTag); - String content = rawTag.substring (tags[kind][0].length ()+1).trim (); - if (kind == 0 || kind==1) { - StringTokenizer tok = new StringTokenizer (content); - String name = ""; - if (tok.hasMoreTokens ()) - name = tok.nextToken (); - String comment = ""; - try { - comment = content.substring (name.length ()+1).trim (); - } catch (StringIndexOutOfBoundsException e) { } - return new DocTag (kind, name, comment); - } else { - return new DocTag (kind, "", content); - } - } + if (kind == -1) { + throw new DocException("unsupported tag type: " + rawTag); + } - private static int getTagNumber (String rawTag) { - rawTag = rawTag.trim ().toLowerCase (); - for (int i=0; iserver.properties by setting extensions = - * packagename.classname, packagename.classname. - */ - + * Helma extensions have to subclass this. The extensions to be loaded are + * defined in server.properties by setting extensions = + * packagename.classname, packagename.classname. + */ public abstract class HelmaExtension { + /** + * called by the Server at startup time. should check wheter the needed classes + * are present and throw a ConfigurationException if not. + */ + public abstract void init(Server server) throws ConfigurationException; - /** - * called by the Server at startup time. should check wheter the needed classes - * are present and throw a ConfigurationException if not. - */ - public abstract void init (Server server) throws ConfigurationException; + /** + * called when an Application is started. This should be synchronized when + * any self-initialization is performed. + */ + public abstract void applicationStarted(Application app) + throws ConfigurationException; - /** - * called when an Application is started. This should be synchronized when - * any self-initialization is performed. - */ - public abstract void applicationStarted (Application app) throws ConfigurationException; + /** + * called when an Application is stopped. + * This should be synchronized when any self-destruction is performed. + */ + public abstract void applicationStopped(Application app); - /** - * called when an Application is stopped. - * This should be synchronized when any self-destruction is performed. - */ - public abstract void applicationStopped (Application app); + /** + * called when an Application's properties are have been updated. + * note that this will be called at startup once *before* applicationStarted(). + */ + public abstract void applicationUpdated(Application app); - /** - * called when an Application's properties are have been updated. - * note that this will be called at startup once *before* applicationStarted(). - */ - public abstract void applicationUpdated (Application app); - - /** - * called by the ScriptingEngine when it is initizalized. Throws a ConfigurationException - * when this type of ScriptingEngine is not supported. New methods and prototypes can be - * added to the scripting environment. New global vars should be returned in a HashMap - * with pairs of varname and ESObjects. This method should be synchronized, if it - * performs any other self-initialization outside the scripting environment. - */ - public abstract HashMap initScripting (Application app, ScriptingEngine engine) throws ConfigurationException; - - public abstract String getName (); + /** + * called by the ScriptingEngine when it is initizalized. Throws a ConfigurationException + * when this type of ScriptingEngine is not supported. New methods and prototypes can be + * added to the scripting environment. New global vars should be returned in a HashMap + * with pairs of varname and ESObjects. This method should be synchronized, if it + * performs any other self-initialization outside the scripting environment. + */ + public abstract HashMap initScripting(Application app, ScriptingEngine engine) + throws ConfigurationException; + /** + * + * + * @return ... + */ + public abstract String getName(); } - diff --git a/src/helma/extensions/demo/DemoExtension.java b/src/helma/extensions/demo/DemoExtension.java index 2a946e06..115a6444 100644 --- a/src/helma/extensions/demo/DemoExtension.java +++ b/src/helma/extensions/demo/DemoExtension.java @@ -1,12 +1,21 @@ +/* + * 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.extensions.demo; -import java.util.HashMap; - -import helma.extensions.HelmaExtension; -import helma.extensions.ConfigurationException; -import helma.framework.core.Application; -import helma.main.Server; -import helma.scripting.ScriptingEngine; // fesi-related stuff: import FESI.Data.ESObject; @@ -14,55 +23,104 @@ import FESI.Data.ESWrapper; import FESI.Data.GlobalObject; import FESI.Exceptions.EcmaScriptException; import FESI.Interpreter.Evaluator; +import helma.extensions.ConfigurationException; +import helma.extensions.HelmaExtension; +import helma.framework.core.Application; +import helma.main.Server; +import helma.scripting.ScriptingEngine; import helma.scripting.fesi.FesiEngine; - +import java.util.HashMap; /** - * a demo extension implementation, to activate this add extensions = - * helma.extensions.demo.DemoExtensions to your server.properties. - * a new global object demo that wraps helma.main.Server - * will be added to the scripting environment. - */ - + * a demo extension implementation, to activate this add extensions = + * helma.extensions.demo.DemoExtensions to your server.properties. + * a new global object demo that wraps helma.main.Server + * will be added to the scripting environment. + */ public class DemoExtension extends HelmaExtension { + /** + * + * + * @param server ... + * + * @throws ConfigurationException ... + */ + public void init(Server server) throws ConfigurationException { + try { + // just a demo with the server class itself (which is always there, obviously) + Class check = Class.forName("helma.main.Server"); + } catch (ClassNotFoundException e) { + throw new ConfigurationException("helma-library not present in classpath. make sure helma.jar is included. get it from http://www.helma.org/"); + } + } - public void init (Server server) throws ConfigurationException { - try { - // just a demo with the server class itself (which is always there, obviously) - Class check = Class.forName("helma.main.Server"); - } catch (ClassNotFoundException e) { - throw new ConfigurationException("helma-library not present in classpath. make sure helma.jar is included. get it from http://www.helma.org/"); - } - } + /** + * + * + * @param app ... + * + * @throws ConfigurationException ... + */ + public void applicationStarted(Application app) throws ConfigurationException { + app.logEvent("DemoExtension init with app " + app.getName()); + } - public void applicationStarted (Application app) throws ConfigurationException { - app.logEvent ("DemoExtension init with app " + app.getName () ); - } + /** + * + * + * @param app ... + */ + public void applicationStopped(Application app) { + app.logEvent("DemoExtension stopped on app " + app.getName()); + } - public void applicationStopped (Application app) { - app.logEvent ("DemoExtension stopped on app " + app.getName () ); - } + /** + * + * + * @param app ... + */ + public void applicationUpdated(Application app) { + app.logEvent("DemoExtension updated on app " + app.getName()); + } - public void applicationUpdated (Application app) { - app.logEvent ("DemoExtension updated on app " + app.getName () ); - } + /** + * + * + * @param app ... + * @param engine ... + * + * @return ... + * + * @throws ConfigurationException ... + */ + public HashMap initScripting(Application app, ScriptingEngine engine) + throws ConfigurationException { + if (!(engine instanceof FesiEngine)) { + throw new ConfigurationException("scripting engine " + engine.toString() + + " not supported in DemoExtension"); + } - public HashMap initScripting (Application app, ScriptingEngine engine) throws ConfigurationException { - if (!(engine instanceof FesiEngine)) - throw new ConfigurationException ("scripting engine " + engine.toString () + " not supported in DemoExtension"); - app.logEvent("initScripting DemoExtension with " + app.getName () + " and " + engine.toString() ); - // fesi-specific code: - Evaluator evaluator = ((FesiEngine)engine).getEvaluator (); - // initialize prototypes and global vars here, but don't add them to fesi's global object - ESWrapper demo = new ESWrapper(Server.getServer (), evaluator); - HashMap globals = new HashMap (); - globals.put ("demo",demo); - return globals; - } + app.logEvent("initScripting DemoExtension with " + app.getName() + " and " + + engine.toString()); - public String getName () { - return "DemoExtension"; - } + // fesi-specific code: + Evaluator evaluator = ((FesiEngine) engine).getEvaluator(); + // initialize prototypes and global vars here, but don't add them to fesi's global object + ESWrapper demo = new ESWrapper(Server.getServer(), evaluator); + HashMap globals = new HashMap(); + + globals.put("demo", demo); + + return globals; + } + + /** + * + * + * @return ... + */ + public String getName() { + return "DemoExtension"; + } } - diff --git a/src/helma/framework/ApplicationStoppedException.java b/src/helma/framework/ApplicationStoppedException.java index b1bbb0ac..a07f20fd 100644 --- a/src/helma/framework/ApplicationStoppedException.java +++ b/src/helma/framework/ApplicationStoppedException.java @@ -1,19 +1,31 @@ -// ApplicationStoppedException.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework; + /** * This is thrown when a request is made to a stopped * application */ - public class ApplicationStoppedException extends RuntimeException { - - - public ApplicationStoppedException () { - super ("The application has been stopped"); + /** + * Creates a new ApplicationStoppedException object. + */ + public ApplicationStoppedException() { + super("The application has been stopped"); } - - } diff --git a/src/helma/framework/CookieTrans.java b/src/helma/framework/CookieTrans.java index ccb61d64..bf60d715 100644 --- a/src/helma/framework/CookieTrans.java +++ b/src/helma/framework/CookieTrans.java @@ -1,4 +1,18 @@ -// CookieTrans.java +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework; @@ -7,68 +21,110 @@ import java.util.HashMap; import javax.servlet.http.Cookie; /** - * Cookie Transmitter. A simple, serializable representation + * Cookie Transmitter. A simple, serializable representation * of an HTTP cookie. */ public final class CookieTrans implements Serializable { - - String name, value, path, domain; + String name; + String value; + String path; + String domain; int days; - CookieTrans (String name, String value) { - this.name = name; - this.value = value; + CookieTrans(String name, String value) { + this.name = name; + this.value = value; } - void setValue (String value) { - this.value = value; + void setValue(String value) { + this.value = value; } - void setDays (int days) { - this.days = days; + void setDays(int days) { + this.days = days; } - void setPath (String path) { - this.path = path; + void setPath(String path) { + this.path = path; } - void setDomain (String domain) { - this.domain = domain; + void setDomain(String domain) { + this.domain = domain; } + /** + * + * + * @return ... + */ public String getName() { - return name; + return name; } + /** + * + * + * @return ... + */ public String getValue() { - return value; + return value; } + /** + * + * + * @return ... + */ public int getDays() { - return days; + return days; } - public String getPath () { - return path; + /** + * + * + * @return ... + */ + public String getPath() { + return path; } - public String getDomain () { - return domain; + /** + * + * + * @return ... + */ + public String getDomain() { + return domain; } - public Cookie getCookie (String defaultPath, String defaultDomain) { - Cookie c = new Cookie (name, value); - if (days > 0) - // Cookie time to live, days -> seconds - c.setMaxAge (days*60*60*24); - if (path != null) - c.setPath (path); - else if (defaultPath != null) - c.setPath (defaultPath); - if (domain != null) - c.setDomain (domain); - else if (defaultDomain != null) - c.setDomain (defaultDomain); - return c; + /** + * + * + * @param defaultPath ... + * @param defaultDomain ... + * + * @return ... + */ + public Cookie getCookie(String defaultPath, String defaultDomain) { + Cookie c = new Cookie(name, value); + + if (days > 0) { + // Cookie time to live, days -> seconds + c.setMaxAge(days * 60 * 60 * 24); + } + + if (path != null) { + c.setPath(path); + } else if (defaultPath != null) { + c.setPath(defaultPath); + } + + if (domain != null) { + c.setDomain(domain); + } else if (defaultDomain != null) { + c.setDomain(defaultDomain); + } + + return c; } } diff --git a/src/helma/framework/FrameworkException.java b/src/helma/framework/FrameworkException.java index 847a497b..be0a48a0 100644 --- a/src/helma/framework/FrameworkException.java +++ b/src/helma/framework/FrameworkException.java @@ -1,17 +1,33 @@ -// FrameworkException.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework; + /** - * The basic exception class used to tell when certain things go + * The basic exception class used to tell when certain things go * wrong in evaluation of requests. */ - public class FrameworkException extends RuntimeException { - - public FrameworkException (String msg) { - super (msg); + /** + * Creates a new FrameworkException object. + * + * @param msg ... + */ + public FrameworkException(String msg) { + super(msg); } - } diff --git a/src/helma/framework/IPathElement.java b/src/helma/framework/IPathElement.java index 7e0dd063..b56289e5 100644 --- a/src/helma/framework/IPathElement.java +++ b/src/helma/framework/IPathElement.java @@ -1,6 +1,19 @@ -// IPathElement.java -// Copyright (c) Hannes Wallnöfer 2001 - +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.framework; @@ -14,34 +27,26 @@ package helma.framework; * parent element.

* */ - public interface IPathElement { - /** * Return the name to be used to get this element from its parent */ - public String getElementName (); + public String getElementName(); /** * Retrieve a child element of this object by name. */ - public IPathElement getChildElement (String name); + public IPathElement getChildElement(String name); /** * Return the parent element of this object. */ - public IPathElement getParentElement (); - + public IPathElement getParentElement(); /** * Get the name of the prototype to be used for this object. This will * determine which scripts, actions and skins can be called on it * within the Helma scripting and rendering framework. */ - public String getPrototype (); - + public String getPrototype(); } - - - - diff --git a/src/helma/framework/IRemoteApp.java b/src/helma/framework/IRemoteApp.java index 3c1c18cb..b4ce5481 100644 --- a/src/helma/framework/IRemoteApp.java +++ b/src/helma/framework/IRemoteApp.java @@ -1,5 +1,18 @@ -// IRemoteApp.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework; @@ -9,11 +22,22 @@ import java.util.Vector; /** * RMI interface for an application. Currently only execute is used and supported. */ - public interface IRemoteApp extends Remote { + /** + * + * + * @param param ... + * + * @return ... + * + * @throws RemoteException ... + */ + public ResponseTrans execute(RequestTrans param) throws RemoteException; - public ResponseTrans execute (RequestTrans param) throws RemoteException; - - public void ping () throws RemoteException; - - } + /** + * + * + * @throws RemoteException ... + */ + public void ping() throws RemoteException; +} diff --git a/src/helma/framework/RedirectException.java b/src/helma/framework/RedirectException.java index 1e43d979..4f29622f 100644 --- a/src/helma/framework/RedirectException.java +++ b/src/helma/framework/RedirectException.java @@ -1,5 +1,18 @@ -// RedirectException.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework; @@ -8,26 +21,43 @@ package helma.framework; * RedirectException is thrown internally when a response is redirected to a * new URL. */ - public class RedirectException extends RuntimeException { - String url; - public RedirectException (String url) { - super ("Redirection Request to "+url); - this.url = url; + /** + * Creates a new RedirectException object. + * + * @param url ... + */ + public RedirectException(String url) { + super("Redirection Request to " + url); + this.url = url; } - public String getMessage () { - return url; + /** + * + * + * @return ... + */ + public String getMessage() { + return url; } + /** + * + * + * @param s ... + */ public void printStackTrace(java.io.PrintStream s) { - // do nothing + // do nothing } + /** + * + * + * @param w ... + */ public void printStackTrace(java.io.PrintWriter w) { - // do nothing + // do nothing } - } diff --git a/src/helma/framework/RequestBean.java b/src/helma/framework/RequestBean.java index d2589ad0..d49e69eb 100644 --- a/src/helma/framework/RequestBean.java +++ b/src/helma/framework/RequestBean.java @@ -1,74 +1,138 @@ +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.framework; +import helma.framework.core.Application; import java.io.Serializable; +import java.util.Date; import java.util.Map; -import helma.framework.core.Application; -import java.util.Date; - +/** + * + */ public class RequestBean implements Serializable { + RequestTrans req; - RequestTrans req; + /** + * Creates a new RequestBean object. + * + * @param req ... + */ + public RequestBean(RequestTrans req) { + this.req = req; + } - public RequestBean(RequestTrans req) { - this.req = req; - } + /** + * + * + * @param name ... + * + * @return ... + */ + public Object get(String name) { + return req.get(name); + } - public Object get (String name) { - return req.get (name); - } + /** + * + * + * @return ... + */ + public boolean isGet() { + return req.isGet(); + } - public boolean isGet () { - return req.isGet (); - } + /** + * + * + * @return ... + */ + public boolean isPost() { + return req.isPost(); + } - public boolean isPost () { - return req.isPost (); - } + /** + * + * + * @return ... + */ + public String toString() { + return "[Request]"; + } - public String toString() { - return "[Request]"; - } + // property related methods: + public String getaction() { + return req.action; + } - // property related methods: + /** + * + * + * @return ... + */ + public Map getdata() { + return req.getRequestData(); + } - public String getaction () { - return req.action; - } + /** + * + * + * @return ... + */ + public long getruntime() { + return (System.currentTimeMillis() - req.startTime); + } - public Map getdata () { - return req.getRequestData (); - } + /** + * + * + * @return ... + */ + public String getpassword() { + return req.getPassword(); + } - public long getruntime () { - return (System.currentTimeMillis() - req.startTime); - } + /** + * + * + * @return ... + */ + public String getpath() { + return req.path; + } - public String getpassword () { - return req.getPassword (); - } + /** + * + * + * @return ... + */ + public String getusername() { + return req.getUsername(); + } - public String getpath () { - return req.path; - } - - public String getusername () { - return req.getUsername (); - } - - /* public Date getLastModified () { - long since = req.getIfModifiedSince (); - if (since < 0) - return null; - else - return new Date (since); - } - - public void setLastModified () { - throw new RuntimeException ("The lastModified property of the Request object is read-only. "+ - "Set lastModified on the Response object if you want to mark the last modification date of a resource."); - } */ + /* public Date getLastModified () { + long since = req.getIfModifiedSince (); + if (since < 0) + return null; + else + return new Date (since); + } + public void setLastModified () { + throw new RuntimeException ("The lastModified property of the Request object is read-only. "+ + "Set lastModified on the Response object if you want to mark the last modification date of a resource."); + } */ } - - - diff --git a/src/helma/framework/RequestTrans.java b/src/helma/framework/RequestTrans.java index 89a33acf..3b4e8ba9 100644 --- a/src/helma/framework/RequestTrans.java +++ b/src/helma/framework/RequestTrans.java @@ -1,31 +1,48 @@ -// RequestTrans.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework; -import java.io.*; -import java.util.*; import helma.objectmodel.*; import helma.util.Base64; +import java.io.*; +import java.util.*; /** - * A Transmitter for a request from the servlet client. Objects of this - * class are directly exposed to JavaScript as global property req. + * A Transmitter for a request from the servlet client. Objects of this + * class are directly exposed to JavaScript as global property req. */ - public class RequestTrans implements Externalizable { + static final long serialVersionUID = 5398880083482000580L; // the uri path of the request public String path; + // the request's session id public String session; + // the map of form and cookie data private Map values; + // the request method - 0 for GET, 1 for POST private byte httpMethod = 0; // timestamp of client-cached version, if present in request private long ifModifiedSince = -1; + // set of ETags the client sent with If-None-Match header private Set etags; @@ -34,185 +51,237 @@ public class RequestTrans implements Externalizable { // the name of the action being invoked public transient String action; - private transient String httpUsername; private transient String httpPassword; - static final long serialVersionUID = 5398880083482000580L; - /** * Create a new Request transmitter with an empty data map. */ - public RequestTrans () { - httpMethod = 0; - values = new HashMap (); + public RequestTrans() { + httpMethod = 0; + values = new HashMap(); } /** - * Create a new request transmitter with the given data map. - */ - public RequestTrans (byte method) { - httpMethod = method; - values = new HashMap (); + * Create a new request transmitter with the given data map. + */ + public RequestTrans(byte method) { + httpMethod = method; + values = new HashMap(); } /** * Set a parameter value in this request transmitter. */ - public void set (String name, Object value) { - values.put (name, value); + public void set(String name, Object value) { + values.put(name, value); } - /** * Get a value from the requests map by key. */ - public Object get (String name) { - try { - return values.get (name); - } catch (Exception x) { - return null; - } + public Object get(String name) { + try { + return values.get(name); + } catch (Exception x) { + return null; + } } /** * Get the data map for this request transmitter. */ - public Map getRequestData () { - return values; + public Map getRequestData() { + return values; } /** * The hash code is computed from the session id if available. This is used to * detect multiple identic requests. */ - public int hashCode () { - return session == null ? super.hashCode () : session.hashCode (); + public int hashCode() { + return (session == null) ? super.hashCode() : session.hashCode(); } - /** * A request is considered equal to another one if it has the same user, path, * and request data. This is used to evaluate multiple simultanous requests only once */ - public boolean equals (Object what) { - try { - RequestTrans other = (RequestTrans) what; - return (session.equals (other.session) && - path.equalsIgnoreCase (other.path) && - values.equals (other.getRequestData ())); - } catch (Exception x) { - return false; - } + public boolean equals(Object what) { + try { + RequestTrans other = (RequestTrans) what; + + return (session.equals(other.session) && path.equalsIgnoreCase(other.path) && + values.equals(other.getRequestData())); + } catch (Exception x) { + return false; + } } /** * Return true if this object represents a HTTP GET Request. */ - public boolean isGet () { - return httpMethod == 0; + public boolean isGet() { + return httpMethod == 0; } /** * Return true if this object represents a HTTP GET Request. */ - public boolean isPost () { - return httpMethod == 1; + public boolean isPost() { + return httpMethod == 1; } /** * Custom externalization code for quicker serialization. */ - public void readExternal (ObjectInput s) throws ClassNotFoundException, IOException { - path = s.readUTF (); - session = s.readUTF (); - values = (Map) s.readObject (); - httpMethod = s.readByte (); - ifModifiedSince = s.readLong (); - etags = (Set) s.readObject (); + public void readExternal(ObjectInput s) throws ClassNotFoundException, IOException { + path = s.readUTF(); + session = s.readUTF(); + values = (Map) s.readObject(); + httpMethod = s.readByte(); + ifModifiedSince = s.readLong(); + etags = (Set) s.readObject(); } /** * Custom externalization code for quicker serialization. */ - public void writeExternal (ObjectOutput s) throws IOException { - s.writeUTF (path); - s.writeUTF (session); - s.writeObject (values); - s.writeByte (httpMethod); - s.writeLong (ifModifiedSince); - s.writeObject (etags); + public void writeExternal(ObjectOutput s) throws IOException { + s.writeUTF(path); + s.writeUTF(session); + s.writeObject(values); + s.writeByte(httpMethod); + s.writeLong(ifModifiedSince); + s.writeObject(etags); } - public void setIfModifiedSince (long since) { - ifModifiedSince = since; + /** + * + * + * @param since ... + */ + public void setIfModifiedSince(long since) { + ifModifiedSince = since; } - public long getIfModifiedSince () { - return ifModifiedSince; + /** + * + * + * @return ... + */ + public long getIfModifiedSince() { + return ifModifiedSince; } - public void setETags (String etagHeader) { - etags = new HashSet(); - if (etagHeader.indexOf (",") > -1) { - StringTokenizer st = new StringTokenizer (etagHeader, ", \r\n"); - while (st.hasMoreTokens()) - etags.add (st.nextToken ()); - } else { - etags.add (etagHeader); - } + /** + * + * + * @param etagHeader ... + */ + public void setETags(String etagHeader) { + etags = new HashSet(); + + if (etagHeader.indexOf(",") > -1) { + StringTokenizer st = new StringTokenizer(etagHeader, ", \r\n"); + + while (st.hasMoreTokens()) + etags.add(st.nextToken()); + } else { + etags.add(etagHeader); + } } - public Set getETags () { - return etags; + /** + * + * + * @return ... + */ + public Set getETags() { + return etags; } - public boolean hasETag (String etag) { - if (etags == null || etag == null) - return false; - return etags.contains (etag); + /** + * + * + * @param etag ... + * + * @return ... + */ + public boolean hasETag(String etag) { + if ((etags == null) || (etag == null)) { + return false; + } + + return etags.contains(etag); } + /** + * + * + * @return ... + */ public String getUsername() { - if ( httpUsername!=null ) - return httpUsername; - String auth = (String)get("authorization"); - if ( auth==null || "".equals(auth) ) { - return null; - } - decodeHttpAuth(auth); - return httpUsername; + if (httpUsername != null) { + return httpUsername; + } + + String auth = (String) get("authorization"); + + if ((auth == null) || "".equals(auth)) { + return null; + } + + decodeHttpAuth(auth); + + return httpUsername; } - public String getPassword() { - if ( httpPassword!=null ) - return httpPassword; - String auth = (String)get("authorization"); - if ( auth==null || "".equals(auth) ) { - return null; - } - decodeHttpAuth(auth); - return httpPassword; + /** + * + * + * @return ... + */ + public String getPassword() { + if (httpPassword != null) { + return httpPassword; + } + + String auth = (String) get("authorization"); + + if ((auth == null) || "".equals(auth)) { + return null; + } + + decodeHttpAuth(auth); + + return httpPassword; } - private void decodeHttpAuth(String auth) { - if ( auth==null ) - return; - StringTokenizer tok; - if( auth.startsWith("Basic ") ) - tok = new StringTokenizer ( new String( Base64.decode((auth.substring(6)).toCharArray()) ), ":" ); - else - tok = new StringTokenizer ( new String( Base64.decode(auth.toCharArray()) ), ":" ); - try { - httpUsername = tok.nextToken(); - } catch ( NoSuchElementException e ) { - httpUsername = null; - } - try { - httpPassword = tok.nextToken(); - } catch ( NoSuchElementException e ) { - httpPassword = null; - } - } + private void decodeHttpAuth(String auth) { + if (auth == null) { + return; + } + StringTokenizer tok; + + if (auth.startsWith("Basic ")) { + tok = new StringTokenizer(new String(Base64.decode((auth.substring(6)).toCharArray())), + ":"); + } else { + tok = new StringTokenizer(new String(Base64.decode(auth.toCharArray())), ":"); + } + + try { + httpUsername = tok.nextToken(); + } catch (NoSuchElementException e) { + httpUsername = null; + } + + try { + httpPassword = tok.nextToken(); + } catch (NoSuchElementException e) { + httpPassword = null; + } + } } diff --git a/src/helma/framework/ResponseBean.java b/src/helma/framework/ResponseBean.java index b3803a9c..602b64a1 100644 --- a/src/helma/framework/ResponseBean.java +++ b/src/helma/framework/ResponseBean.java @@ -1,175 +1,386 @@ +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.framework; +import helma.framework.core.Application; import java.io.Serializable; +import java.util.Date; import java.util.Map; -import helma.framework.core.Application; -import java.util.Date; - +/** + * + */ public class ResponseBean implements Serializable { - ResponseTrans res; - public ResponseBean(ResponseTrans res) { - this.res = res; + /** + * Creates a new ResponseBean object. + * + * @param res ... + */ + public ResponseBean(ResponseTrans res) { + this.res = res; } - public void encode (Object what) { - res.encode (what); + /** + * + * + * @param what ... + */ + public void encode(Object what) { + res.encode(what); } - public void encodeXml (Object what) { - res.encodeXml (what); + /** + * + * + * @param what ... + */ + public void encodeXml(Object what) { + res.encodeXml(what); } - public void format (Object what) { - res.format (what); + /** + * + * + * @param what ... + */ + public void format(Object what) { + res.format(what); } - public void redirect (String url) throws RedirectException { - res.redirect (url); + /** + * + * + * @param url ... + * + * @throws RedirectException ... + */ + public void redirect(String url) throws RedirectException { + res.redirect(url); } - public void reset () { - res.reset (); + /** + * + */ + public void reset() { + res.reset(); } - public void setCookie (String key, String value) { - res.setCookie (key, value, -1, null, null); + /** + * + * + * @param key ... + * @param value ... + */ + public void setCookie(String key, String value) { + res.setCookie(key, value, -1, null, null); } - public void setCookie (String key, String value, int days) { - res.setCookie (key, value, days, null, null); - } - - public void setCookie (String key, String value, int days, String path) { - res.setCookie (key, value, days, path, null); + /** + * + * + * @param key ... + * @param value ... + * @param days ... + */ + public void setCookie(String key, String value, int days) { + res.setCookie(key, value, days, null, null); } - public void setCookie (String key, String value, int days, String path, String domain) { - res.setCookie (key, value, days, path, domain); + /** + * + * + * @param key ... + * @param value ... + * @param days ... + * @param path ... + */ + public void setCookie(String key, String value, int days, String path) { + res.setCookie(key, value, days, path, null); } - public void write (Object what) { - res.write (what); + /** + * + * + * @param key ... + * @param value ... + * @param days ... + * @param path ... + * @param domain ... + */ + public void setCookie(String key, String value, int days, String path, String domain) { + res.setCookie(key, value, days, path, domain); } - public void writeln (Object what) { - res.writeln (what); + /** + * + * + * @param what ... + */ + public void write(Object what) { + res.write(what); } - public void writeBinary (byte[] what) { - res.writeBinary (what); + /** + * + * + * @param what ... + */ + public void writeln(Object what) { + res.writeln(what); } - public void debug (Object message) { - res.debug (message); + /** + * + * + * @param what ... + */ + public void writeBinary(byte[] what) { + res.writeBinary(what); } + /** + * + * + * @param message ... + */ + public void debug(Object message) { + res.debug(message); + } + + /** + * + * + * @return ... + */ public String toString() { - return "[Response]"; + return "[Response]"; } - - // property-related methods: - - public boolean getcache () { - return res.cache; + // property-related methods: + public boolean getcache() { + return res.cache; } - public void setcache (boolean cache) { - res.cache = cache; + /** + * + * + * @param cache ... + */ + public void setcache(boolean cache) { + res.cache = cache; } - public String getcharset () { - return res.charset; + /** + * + * + * @return ... + */ + public String getcharset() { + return res.charset; } - public void setcharset (String charset) { - res.charset = charset; + /** + * + * + * @param charset ... + */ + public void setcharset(String charset) { + res.charset = charset; } - public String getcontentType () { - return res.contentType; + /** + * + * + * @return ... + */ + public String getcontentType() { + return res.contentType; } - public void setcontentType (String contentType) { - res.contentType = contentType; + /** + * + * + * @param contentType ... + */ + public void setcontentType(String contentType) { + res.contentType = contentType; } - public Map getdata () { - return res.getResponseData (); - } - - public Map gethandlers () { - return res.getMacroHandlers (); + /** + * + * + * @return ... + */ + public Map getdata() { + return res.getResponseData(); } - public String geterror () { - return res.error; + /** + * + * + * @return ... + */ + public Map gethandlers() { + return res.getMacroHandlers(); } - public String getmessage () { - return res.message; + /** + * + * + * @return ... + */ + public String geterror() { + return res.error; } - public void setmessage (String message) { - res.message = message; + /** + * + * + * @return ... + */ + public String getmessage() { + return res.message; } - public String getrealm () { - return res.realm; + /** + * + * + * @param message ... + */ + public void setmessage(String message) { + res.message = message; } - public void setrealm (String realm) { - res.realm = realm; + /** + * + * + * @return ... + */ + public String getrealm() { + return res.realm; } - public void setskinpath (Object[] arr) { - res.setSkinpath (arr); + /** + * + * + * @param realm ... + */ + public void setrealm(String realm) { + res.realm = realm; } - public Object[] getskinpath () { - return res.getSkinpath (); + /** + * + * + * @param arr ... + */ + public void setskinpath(Object[] arr) { + res.setSkinpath(arr); } - public int getstatus () { - return res.status; + /** + * + * + * @return ... + */ + public Object[] getskinpath() { + return res.getSkinpath(); } - public void setstatus (int status) { - res.status = status; + /** + * + * + * @return ... + */ + public int getstatus() { + return res.status; } - public Date getLastModified () { - long modified = res.getLastModified (); - if (modified > -1) - return new Date (modified); - else - return null; + /** + * + * + * @param status ... + */ + public void setstatus(int status) { + res.status = status; } - public void setLastModified (Date date) { - if (date == null) - res.setLastModified (-1); - else - res.setLastModified (date.getTime()); + /** + * + * + * @return ... + */ + public Date getLastModified() { + long modified = res.getLastModified(); + + if (modified > -1) { + return new Date(modified); + } else { + return null; + } } - public String getETag () { - return res.getETag (); - } - - public void setETag (String etag) { - res.setETag (etag); + /** + * + * + * @param date ... + */ + public void setLastModified(Date date) { + if (date == null) { + res.setLastModified(-1); + } else { + res.setLastModified(date.getTime()); + } } - public void dependsOn (Object what) { - res.dependsOn (what); + /** + * + * + * @return ... + */ + public String getETag() { + return res.getETag(); } - public void digest () { - res.digestDependencies (); + /** + * + * + * @param etag ... + */ + public void setETag(String etag) { + res.setETag(etag); + } + + /** + * + * + * @param what ... + */ + public void dependsOn(Object what) { + res.dependsOn(what); + } + + /** + * + */ + public void digest() { + res.digestDependencies(); } ///////////////////////////////////// @@ -177,14 +388,16 @@ public class ResponseBean implements Serializable { // Helma templates (*.hsp files) and shouldn't // be used otherwise. //////////////////////////////////// - - public void pushStringBuffer () { - res.pushStringBuffer (); + public void pushStringBuffer() { + res.pushStringBuffer(); } - public String popStringBuffer () { - return res.popStringBuffer (); + /** + * + * + * @return ... + */ + public String popStringBuffer() { + return res.popStringBuffer(); } - } - diff --git a/src/helma/framework/ResponseTrans.java b/src/helma/framework/ResponseTrans.java index e9cea874..c40b0c56 100644 --- a/src/helma/framework/ResponseTrans.java +++ b/src/helma/framework/ResponseTrans.java @@ -1,21 +1,35 @@ -// ResponseTrans.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework; -import java.io.*; -import java.util.*; import helma.framework.core.Skin; import helma.objectmodel.*; import helma.util.*; +import java.io.*; import java.security.*; +import java.util.*; /** - * A Transmitter for a response to the servlet client. Objects of this - * class are directly exposed to JavaScript as global property res. + * A Transmitter for a response to the servlet client. Objects of this + * class are directly exposed to JavaScript as global property res. */ - public final class ResponseTrans implements Externalizable { + static final long serialVersionUID = -8627370766119740844L; + static final int INITIAL_BUFFER_SIZE = 2048; /** * Set the MIME content type of the response. @@ -50,11 +64,13 @@ public final class ResponseTrans implements Externalizable { // contains the redirect URL private String redir = null; - + // the last-modified date, if it should be set in the response private long lastModified = -1; + // flag to signal that resource has not been modified private boolean notModified = false; + // Entity Tag for this response, used for conditional GETs private String etag = null; @@ -63,19 +79,19 @@ public final class ResponseTrans implements Externalizable { // the buffer used to build the response private transient StringBuffer buffer = null; + // these are used to implement the _as_string variants for Hop templates. private transient Stack buffers; // the path used to tell where to look for skins private transient Object[] skinpath = null; + // hashmap for skin caching private transient HashMap skincache; - + // buffer for debug messages - will be automatically appended to response private transient StringBuffer debugBuffer; - static final long serialVersionUID = -8627370766119740844L; - /** * string fields that hold a user message */ @@ -85,147 +101,169 @@ public final class ResponseTrans implements Externalizable { * string fields that hold an error message */ public transient String error; - - static final int INITIAL_BUFFER_SIZE = 2048; // the map of form and cookie data private transient Map values; // the map of macro handlers private transient Map handlers; - + // the request trans for this response private transient RequestTrans reqtrans; // the message digest used to generate composed digests for ETag headers private transient MessageDigest digest; - + // the appliciation checksum to make ETag headers sensitive to app changes long applicationChecksum; - - public ResponseTrans () { - super (); - message = error = null; - values = new HashMap (); - handlers = new HashMap (); + /** + * Creates a new ResponseTrans object. + */ + public ResponseTrans() { + super(); + message = error = null; + values = new HashMap(); + handlers = new HashMap(); } - public ResponseTrans (RequestTrans req) { - this(); - reqtrans = req; + /** + * Creates a new ResponseTrans object. + * + * @param req ... + */ + public ResponseTrans(RequestTrans req) { + this(); + reqtrans = req; } - /** * Get a value from the responses map by key. */ - public Object get (String name) { - try { - return values.get (name); - } catch (Exception x) { - return null; - } + public Object get(String name) { + try { + return values.get(name); + } catch (Exception x) { + return null; + } } /** * Get the data map for this response transmitter. */ - public Map getResponseData () { - return values; + public Map getResponseData() { + return values; } /** * Get the macro handlers map for this response transmitter. */ - public Map getMacroHandlers () { - return handlers; + public Map getMacroHandlers() { + return handlers; } - /** * Reset the response object to its initial empty state. */ - public void reset () { - if (buffer != null) - buffer.setLength (0); - buffers = null; - response = null; - redir = null; - skin = null; - message = error = null; - values.clear (); - lastModified = -1; - notModified = false; - etag = null; - if (digest != null) - digest.reset(); - } + public void reset() { + if (buffer != null) { + buffer.setLength(0); + } + buffers = null; + response = null; + redir = null; + skin = null; + message = error = null; + values.clear(); + lastModified = -1; + notModified = false; + etag = null; + + if (digest != null) { + digest.reset(); + } + } /** * This is called before a skin is rendered as string (renderSkinAsString) to redirect the output * to a new string buffer. */ - public void pushStringBuffer () { - if (buffers == null) - buffers = new Stack(); - if (buffer != null) - buffers.push (buffer); - buffer = new StringBuffer (64); + public void pushStringBuffer() { + if (buffers == null) { + buffers = new Stack(); + } + + if (buffer != null) { + buffers.push(buffer); + } + + buffer = new StringBuffer(64); } /** * Returns the content of the current string buffer and switches back to the previos one. */ - public String popStringBuffer () { - StringBuffer b = buffer; - buffer = buffers.empty() ? null : (StringBuffer) buffers.pop (); - return b.toString (); + public String popStringBuffer() { + StringBuffer b = buffer; + + buffer = buffers.empty() ? null : (StringBuffer) buffers.pop(); + + return b.toString(); } /** * Get the response buffer, creating it if it doesn't exist */ - public StringBuffer getBuffer () { - if (buffer == null) - buffer = new StringBuffer (INITIAL_BUFFER_SIZE); - return buffer; + public StringBuffer getBuffer() { + if (buffer == null) { + buffer = new StringBuffer(INITIAL_BUFFER_SIZE); + } + + return buffer; } - /** - * Append a string to the response unchanged. This is often called + * Append a string to the response unchanged. This is often called * at the end of a request to write out the whole page, so if buffer * is uninitialized we just set it to the string argument. */ - public void write (Object what) { - if (what != null) { - String str = what.toString (); - if (buffer == null) - buffer = new StringBuffer (Math.max (str.length()+100, INITIAL_BUFFER_SIZE)); - buffer.append (what.toString ()); - } + public void write(Object what) { + if (what != null) { + String str = what.toString(); + + if (buffer == null) { + buffer = new StringBuffer(Math.max(str.length() + 100, INITIAL_BUFFER_SIZE)); + } + + buffer.append(what.toString()); + } } /** * Utility function that appends a
to whatever is written. */ - public void writeln (Object what) { - if (buffer == null) - buffer = new StringBuffer (INITIAL_BUFFER_SIZE); - if (what != null) - buffer.append (what.toString ()); - buffer.append ("
\r\n"); + public void writeln(Object what) { + if (buffer == null) { + buffer = new StringBuffer(INITIAL_BUFFER_SIZE); + } + + if (what != null) { + buffer.append(what.toString()); + } + + buffer.append("
\r\n"); } /** * Append a part from a char array to the response buffer. */ - public void writeCharArray (char[] c, int start, int length) { - if (buffer == null) - buffer = new StringBuffer (Math.max (length, INITIAL_BUFFER_SIZE)); - buffer.append (c, start, length); + public void writeCharArray(char[] c, int start, int length) { + if (buffer == null) { + buffer = new StringBuffer(Math.max(length, INITIAL_BUFFER_SIZE)); + } + + buffer.append(c, start, length); } /** @@ -233,83 +271,114 @@ public final class ResponseTrans implements Externalizable { * that buffer exists and its length is larger than offset. str may be null, in which * case nothing happens. */ - public void debug (Object message) { - if (debugBuffer == null) - debugBuffer = new StringBuffer (); - String str = message == null ? "null" : message.toString (); - debugBuffer.append ("

"); - debugBuffer.append (str); - debugBuffer.append ("

"); + public void debug(Object message) { + if (debugBuffer == null) { + debugBuffer = new StringBuffer(); + } + + String str = (message == null) ? "null" : message.toString(); + + debugBuffer.append("

"); + debugBuffer.append(str); + debugBuffer.append("

"); } /** * Replace special characters with entities, including <, > and ", thus allowing * no HTML tags. */ - public void encode (Object what) { - if (what != null) { - String str = what.toString (); - if (buffer == null) - buffer = new StringBuffer (Math.max (str.length()+100, INITIAL_BUFFER_SIZE)); - HtmlEncoder.encodeAll (str, buffer); - } + public void encode(Object what) { + if (what != null) { + String str = what.toString(); + + if (buffer == null) { + buffer = new StringBuffer(Math.max(str.length() + 100, INITIAL_BUFFER_SIZE)); + } + + HtmlEncoder.encodeAll(str, buffer); + } } /** * Replace special characters with entities but pass through HTML tags */ - public void format (Object what) { - if (what != null) { - String str = what.toString (); - if (buffer == null) - buffer = new StringBuffer (Math.max (str.length()+100, INITIAL_BUFFER_SIZE)); - HtmlEncoder.encode (str, buffer); - } - } + public void format(Object what) { + if (what != null) { + String str = what.toString(); + if (buffer == null) { + buffer = new StringBuffer(Math.max(str.length() + 100, INITIAL_BUFFER_SIZE)); + } + + HtmlEncoder.encode(str, buffer); + } + } /** * Replace special characters with entities, including <, > and ", thus allowing * no HTML tags. */ - public void encodeXml (Object what) { - if (what != null) { - String str = what.toString (); - if (buffer == null) - buffer = new StringBuffer (Math.max (str.length()+100, INITIAL_BUFFER_SIZE)); - HtmlEncoder.encodeXml (str, buffer); - } - } + public void encodeXml(Object what) { + if (what != null) { + String str = what.toString(); + if (buffer == null) { + buffer = new StringBuffer(Math.max(str.length() + 100, INITIAL_BUFFER_SIZE)); + } + + HtmlEncoder.encodeXml(str, buffer); + } + } /** * Encode HTML entities, but leave newlines alone. This is for the content of textarea forms. */ - public void encodeForm (Object what) { - if (what != null) { - String str = what.toString (); - if (buffer == null) - buffer = new StringBuffer (Math.max (str.length()+100, INITIAL_BUFFER_SIZE)); - HtmlEncoder.encodeAll (str, buffer, false); - } + public void encodeForm(Object what) { + if (what != null) { + String str = what.toString(); + + if (buffer == null) { + buffer = new StringBuffer(Math.max(str.length() + 100, INITIAL_BUFFER_SIZE)); + } + + HtmlEncoder.encodeAll(str, buffer, false); + } } + /** + * + * + * @param str ... + */ + public void append(String str) { + if (str != null) { + if (buffer == null) { + buffer = new StringBuffer(Math.max(str.length(), INITIAL_BUFFER_SIZE)); + } - public void append (String str) { - if (str != null) { - if (buffer == null) - buffer = new StringBuffer (Math.max (str.length(), INITIAL_BUFFER_SIZE)); - buffer.append (str); - } + buffer.append(str); + } } - public void redirect (String url) throws RedirectException { - redir = url; - throw new RedirectException (url); + /** + * + * + * @param url ... + * + * @throws RedirectException ... + */ + public void redirect(String url) throws RedirectException { + redir = url; + throw new RedirectException(url); } - public String getRedirect () { - return redir; + /** + * + * + * @return ... + */ + public String getRedirect() { + return redir; } /** @@ -317,251 +386,407 @@ public final class ResponseTrans implements Externalizable { * overwrite the previous output. We take a generic object as parameter to be able to * generate a better error message, but it must be byte[]. */ - public void writeBinary (byte[] what) { - response = what; + public void writeBinary(byte[] what) { + response = what; } - /** * This has to be called after writing to this response has finished and before it is shipped back to the * web server. Transforms the string buffer into a char array to minimize size. */ - public synchronized void close (String cset) throws UnsupportedEncodingException { - // only use default charset if not explicitly set for this response. - if (charset == null) - charset = cset; - // if charset is not set, use western encoding - if (charset == null) - charset = "ISO-8859-1"; - boolean encodingError = false; - // only close if the response hasn't been closed yet - if (response == null) { - // if debug buffer exists, append it to main buffer - if (debugBuffer != null) { - if (buffer == null) - buffer = debugBuffer; - else - buffer.append (debugBuffer); - } - // get the buffer's bytes in the specified encoding - if (buffer != null) { - try { - response = buffer.toString ().getBytes (charset); - } catch (UnsupportedEncodingException uee) { - encodingError = true; - response = buffer.toString ().getBytes (); - } - // make sure this is done only once, even with more requsts attached - buffer = null; - } else { - response = new byte[0]; - } - } - // if etag is not set, calc MD5 digest and check it - if (etag == null && lastModified == -1 && redir == null) try { - digest = MessageDigest.getInstance("MD5"); - // if (contentType != null) - // digest.update (contentType.getBytes()); - byte[] b = digest.digest(response); - etag = "\""+new String (Base64.encode(b))+"\""; - if (reqtrans != null && reqtrans.hasETag (etag)) { - response = new byte[0]; - notModified = true; - } - } catch (Exception ignore) { - // Etag creation failed for some reason. Ignore. - } - notifyAll (); - // if there was a problem with the encoding, let the app know - if (encodingError) - throw new UnsupportedEncodingException (charset); + public synchronized void close(String cset) throws UnsupportedEncodingException { + // only use default charset if not explicitly set for this response. + if (charset == null) { + charset = cset; + } + + // if charset is not set, use western encoding + if (charset == null) { + charset = "ISO-8859-1"; + } + + boolean encodingError = false; + + // only close if the response hasn't been closed yet + if (response == null) { + // if debug buffer exists, append it to main buffer + if (debugBuffer != null) { + if (buffer == null) { + buffer = debugBuffer; + } else { + buffer.append(debugBuffer); + } + } + + // get the buffer's bytes in the specified encoding + if (buffer != null) { + try { + response = buffer.toString().getBytes(charset); + } catch (UnsupportedEncodingException uee) { + encodingError = true; + response = buffer.toString().getBytes(); + } + + // make sure this is done only once, even with more requsts attached + buffer = null; + } else { + response = new byte[0]; + } + } + + // if etag is not set, calc MD5 digest and check it + if ((etag == null) && (lastModified == -1) && (redir == null)) { + try { + digest = MessageDigest.getInstance("MD5"); + + // if (contentType != null) + // digest.update (contentType.getBytes()); + byte[] b = digest.digest(response); + + etag = "\"" + new String(Base64.encode(b)) + "\""; + + if ((reqtrans != null) && reqtrans.hasETag(etag)) { + response = new byte[0]; + notModified = true; + } + } catch (Exception ignore) { + // Etag creation failed for some reason. Ignore. + } + } + + notifyAll(); + + // if there was a problem with the encoding, let the app know + if (encodingError) { + throw new UnsupportedEncodingException(charset); + } } /** * If we just attached to evaluation we call this instead of close because only the primary thread * is responsible for closing the result */ - public synchronized void waitForClose () { - try { - if (response == null) - wait (10000l); - } catch (InterruptedException ix) {} + public synchronized void waitForClose() { + try { + if (response == null) { + wait(10000L); + } + } catch (InterruptedException ix) { + } } - public byte[] getContent () { - return response == null ? new byte[0] : response; + /** + * + * + * @return ... + */ + public byte[] getContent() { + return (response == null) ? new byte[0] : response; } - public int getContentLength () { - if (response != null) - return response.length; - return 0; + /** + * + * + * @return ... + */ + public int getContentLength() { + if (response != null) { + return response.length; + } + + return 0; } - public String getContentType () { - if (charset != null) - return contentType+"; charset="+charset; - return contentType; + /** + * + * + * @return ... + */ + public String getContentType() { + if (charset != null) { + return contentType + "; charset=" + charset; + } + + return contentType; } - public void setLastModified (long modified) { - if (modified > -1 && reqtrans != null && - reqtrans.getIfModifiedSince() >= modified) - { - notModified = true; - throw new RedirectException (null); - } - lastModified = modified; + /** + * + * + * @param modified ... + */ + public void setLastModified(long modified) { + if ((modified > -1) && (reqtrans != null) && + (reqtrans.getIfModifiedSince() >= modified)) { + notModified = true; + throw new RedirectException(null); + } + + lastModified = modified; } - public long getLastModified () { - return lastModified; + /** + * + * + * @return ... + */ + public long getLastModified() { + return lastModified; } - public void setETag (String value) { - etag = value == null ? null : "\""+value+"\""; - if (etag != null && reqtrans != null && reqtrans.hasETag (etag)) { - notModified = true; - throw new RedirectException (null); - } - } - - public String getETag () { - return etag; - } - - public boolean getNotModified () { - return notModified; + /** + * + * + * @param value ... + */ + public void setETag(String value) { + etag = (value == null) ? null : ("\"" + value + "\""); + + if ((etag != null) && (reqtrans != null) && reqtrans.hasETag(etag)) { + notModified = true; + throw new RedirectException(null); + } } - public void dependsOn (Object what) { - if (digest == null) try { - digest = MessageDigest.getInstance ("MD5"); - } catch (NoSuchAlgorithmException nsa) { - // MD5 should always be available - } - if (what == null) { - digest.update (new byte[0]); - } else if (what instanceof Date) { - digest.update (MD5Encoder.toBytes(((Date) what).getTime())); - } else if (what instanceof byte[]) { - digest.update ((byte[]) what); - } else { - String str = what.toString(); - if (str != null) - digest.update (str.getBytes ()); - else - digest.update (new byte[0]); - } + /** + * + * + * @return ... + */ + public String getETag() { + return etag; } - public void digestDependencies () { - if (digest == null) - return; - byte[] b = digest.digest(MD5Encoder.toBytes (applicationChecksum)); - /* StringBuffer buf = new StringBuffer(b.length*2); - for ( int i=0; i 511) - return false; - int current = allThreads.size(); - synchronized (allThreads) { - if (n > current) { - int toBeCreated = n - current; - for (int i=0; i 511)) { + return false; + } + + int current = allThreads.size(); + + synchronized (allThreads) { + if (n > current) { + int toBeCreated = n - current; + + for (int i = 0; i < toBeCreated; i++) { + RequestEvaluator ev = new RequestEvaluator(this); + + freeThreads.push(ev); + allThreads.addElement(ev); + } + } else if (n < current) { + int toBeDestroyed = current - n; + + for (int i = 0; i < toBeDestroyed; i++) { + try { + RequestEvaluator re = (RequestEvaluator) freeThreads.pop(); + + allThreads.removeElement(re); + + // typemgr.unregisterRequestEvaluator (re); + re.stopThread(); + } catch (EmptyStackException empty) { + return false; + } + } + } + } + + return true; } /** * Return the number of currently active threads */ - public int getActiveThreads () { - return 0; + public int getActiveThreads() { + return 0; } /** * Execute a request coming in from a web client. */ - public ResponseTrans execute (RequestTrans req) { + public ResponseTrans execute(RequestTrans req) { + requestCount += 1; - requestCount += 1; + // get user for this request's session + Session session = checkSession(req.session); - // get user for this request's session - Session session = checkSession (req.session); - session.touch(); + session.touch(); - ResponseTrans res = null; - RequestEvaluator ev = null; - // are we responsible for releasing the evaluator and closing the result? - boolean primaryRequest = false; - try { - // first look if a request with same user/path/data is already being executed. - // if so, attach the request to its output instead of starting a new evaluation - // this helps to cleanly solve "doubleclick" kind of users - ev = (RequestEvaluator) activeRequests.get (req); - if (ev != null) { - res = ev.attachRequest (req); - } - if (res == null) { - primaryRequest = true; - // if attachRequest returns null this means we came too late - // and the other request was finished in the meantime + ResponseTrans res = null; + RequestEvaluator ev = null; - // check if the properties file has been updated - updateProperties (); - // get evaluator and invoke - ev = getEvaluator (); - res = ev.invoke (req, session); - } - } catch (ApplicationStoppedException stopped) { - // let the servlet know that this application has gone to heaven - throw stopped; - } catch (Exception x) { - errorCount += 1; - res = new ResponseTrans (); - res.write ("Error in application: " + x.getMessage () + ""); - } finally { - if (primaryRequest) { - activeRequests.remove (req); - releaseEvaluator (ev); - // response needs to be closed/encoded before sending it back - try { - res.close (charset); - } catch (UnsupportedEncodingException uee) { - logEvent ("Unsupported response encoding: "+uee.getMessage()); - } - } else { - res.waitForClose (); - } - } - return res; + // are we responsible for releasing the evaluator and closing the result? + boolean primaryRequest = false; + + try { + // first look if a request with same user/path/data is already being executed. + // if so, attach the request to its output instead of starting a new evaluation + // this helps to cleanly solve "doubleclick" kind of users + ev = (RequestEvaluator) activeRequests.get(req); + + if (ev != null) { + res = ev.attachRequest(req); + } + + if (res == null) { + primaryRequest = true; + + // if attachRequest returns null this means we came too late + // and the other request was finished in the meantime + // check if the properties file has been updated + updateProperties(); + + // get evaluator and invoke + ev = getEvaluator(); + res = ev.invoke(req, session); + } + } catch (ApplicationStoppedException stopped) { + // let the servlet know that this application has gone to heaven + throw stopped; + } catch (Exception x) { + errorCount += 1; + res = new ResponseTrans(); + res.write("Error in application: " + x.getMessage() + ""); + } finally { + if (primaryRequest) { + activeRequests.remove(req); + releaseEvaluator(ev); + + // response needs to be closed/encoded before sending it back + try { + res.close(charset); + } catch (UnsupportedEncodingException uee) { + logEvent("Unsupported response encoding: " + uee.getMessage()); + } + } else { + res.waitForClose(); + } + } + + return res; } - /** * Called to execute a method via XML-RPC, usally by helma.main.ApplicationManager * which acts as default handler/request dispatcher. */ - public Object executeXmlRpc (String method, Vector args) throws Exception { - xmlrpcCount += 1; - Object retval = null; - RequestEvaluator ev = null; - try { - // check if the properties file has been updated - updateProperties (); - // get evaluator and invoke - ev = getEvaluator (); - retval = ev.invokeXmlRpc (method, args.toArray()); - } finally { - releaseEvaluator (ev); - } - return retval; + public Object executeXmlRpc(String method, Vector args) + throws Exception { + xmlrpcCount += 1; + + Object retval = null; + RequestEvaluator ev = null; + + try { + // check if the properties file has been updated + updateProperties(); + + // get evaluator and invoke + ev = getEvaluator(); + retval = ev.invokeXmlRpc(method, args.toArray()); + } finally { + releaseEvaluator(ev); + } + + return retval; } /** * Reset the application's object cache, causing all objects to be refetched from * the database. */ - public void clearCache () { - nmgr.clearCache (); + public void clearCache() { + nmgr.clearCache(); } /** * Returns the number of elements in the NodeManager's cache */ - public int getCacheUsage () { - return nmgr.countCacheEntries (); + public int getCacheUsage() { + return nmgr.countCacheEntries(); } /** @@ -532,50 +627,59 @@ public final class Application implements IPathElement, Runnable { * with a non-null object, the helma node manager will be bypassed. This function * can be used to script and publish any Java object structure with Helma. */ - public void setDataRoot (Object root) { - this.rootObject = root; + public void setDataRoot(Object root) { + this.rootObject = root; } /** * This method returns the root object of this application's object tree. */ - public Object getDataRoot () { - // check if we have a custom root object class - if (rootObjectClass != null) { - // create custom root element. - if (rootObject == null) { - try { - if ( classMapping.containsKey("root.factory.class") && classMapping.containsKey("root.factory.method") ) { - Class c = typemgr.loader.loadClass( classMapping.getProperty("root.factory.class") ); - Method m = c.getMethod( classMapping.getProperty("root.factory.method"), null ); - rootObject = m.invoke(c, null); - } else { - Class c = typemgr.loader.loadClass( classMapping.getProperty("root") ); - rootObject = c.newInstance(); - } - } catch ( Exception e ) { - throw new RuntimeException ( "Error creating root object: " + e.toString() ); - } - } - return rootObject; - } - // no custom root object is defined - use standard helma objectmodel - else { - // INode root = nmgr.safe.getNode ("0", rootMapping); - // root.setDbMapping (rootMapping); - // rootObject = root; - rootObject = nmgr.safe.getNode ("0", rootMapping); - return rootObject; - } + public Object getDataRoot() { + // check if we have a custom root object class + if (rootObjectClass != null) { + // create custom root element. + if (rootObject == null) { + try { + if (classMapping.containsKey("root.factory.class") && + classMapping.containsKey("root.factory.method")) { + Class c = typemgr.loader.loadClass(classMapping.getProperty("root.factory.class")); + Method m = c.getMethod(classMapping.getProperty("root.factory.method"), + null); + + rootObject = m.invoke(c, null); + } else { + Class c = typemgr.loader.loadClass(classMapping.getProperty("root")); + + rootObject = c.newInstance(); + } + } catch (Exception e) { + throw new RuntimeException("Error creating root object: " + + e.toString()); + } + } + + return rootObject; + } + // no custom root object is defined - use standard helma objectmodel + else { + // INode root = nmgr.safe.getNode ("0", rootMapping); + // root.setDbMapping (rootMapping); + // rootObject = root; + rootObject = nmgr.safe.getNode("0", rootMapping); + + return rootObject; + } } /** * Returns the Object which contains registered users of this application. */ - public INode getUserRoot () { - INode users = nmgr.safe.getNode ("1", userRootMapping); - users.setDbMapping (userRootMapping); - return users; + public INode getUserRoot() { + INode users = nmgr.safe.getNode("1", userRootMapping); + + users.setDbMapping(userRootMapping); + + return users; } /** @@ -583,90 +687,100 @@ public final class Application implements IPathElement, Runnable { * the gateway to the helma.objectmodel packages, which perform the mapping of objects to * relational database tables or the embedded database. */ - public WrappedNodeManager getWrappedNodeManager () { - return nmgr.safe; + public WrappedNodeManager getWrappedNodeManager() { + return nmgr.safe; } /** * Return a transient node that is shared by all evaluators of this application ("app node") */ - public INode getCacheNode () { - return cachenode; + public INode getCacheNode() { + return cachenode; } - /** * Returns a Node representing a registered user of this application by his or her user name. */ - public INode getUserNode (String uid) { - try { - INode users = getUserRoot (); - return users.getNode (uid); - } catch (Exception x) { - return null; - } - } + public INode getUserNode(String uid) { + try { + INode users = getUserRoot(); + return users.getNode(uid); + } catch (Exception x) { + return null; + } + } /** * Return a prototype for a given node. If the node doesn't specify a prototype, * return the generic hopobject prototype. */ - public Prototype getPrototype (Object obj) { - String protoname = getPrototypeName (obj); - if (protoname == null) - return typemgr.getPrototype ("hopobject"); - Prototype p = typemgr.getPrototype (protoname); - if (p == null) - p = typemgr.getPrototype ("hopobject"); - return p; + public Prototype getPrototype(Object obj) { + String protoname = getPrototypeName(obj); + + if (protoname == null) { + return typemgr.getPrototype("hopobject"); + } + + Prototype p = typemgr.getPrototype(protoname); + + if (p == null) { + p = typemgr.getPrototype("hopobject"); + } + + return p; } /** * Return a collection containing all prototypes defined for this application */ - public Collection getPrototypes () { - return typemgr.prototypes.values (); + public Collection getPrototypes() { + return typemgr.prototypes.values(); } /** * Return a skin for a given object. The skin is found by determining the prototype * to use for the object, then looking up the skin for the prototype. */ - public Skin getSkin (Object object, String skinname, Object[] skinpath) { - Prototype proto = getPrototype (object); - if (proto == null) - return null; - return skinmgr.getSkin (proto, skinname, skinpath); + public Skin getSkin(Object object, String skinname, Object[] skinpath) { + Prototype proto = getPrototype(object); + + if (proto == null) { + return null; + } + + return skinmgr.getSkin(proto, skinname, skinpath); } /** * Return the session currently associated with a given Hop session ID. * Create a new session if necessary. */ - public Session checkSession (String sessionID) { - Session session = getSession(sessionID); - if ( session==null ) { - session = new Session (sessionID, this); - sessions.put (sessionID, session); - } - return session; + public Session checkSession(String sessionID) { + Session session = getSession(sessionID); + + if (session == null) { + session = new Session(sessionID, this); + sessions.put(sessionID, session); + } + + return session; } /** * Remove the session from the sessions-table and logout the user. */ - public void destroySession (String sessionID) { - logoutSession (getSession (sessionID)); - sessions.remove (sessionID); + public void destroySession(String sessionID) { + logoutSession(getSession(sessionID)); + sessions.remove(sessionID); } /** * Remove the session from the sessions-table and logout the user. */ - public void destroySession (Session session) { - logoutSession (session); - sessions.remove (session.getSessionID ()); + public void destroySession(Session session) { + logoutSession(session); + sessions.remove(session.getSessionID()); } /** @@ -674,158 +788,194 @@ public final class Application implements IPathElement, Runnable { * actual changes from the table itself, which is managed by the application. * It is safe and allowed to manipulate the session objects contained in the table, though. */ - public Map getSessions () { - return (Map) sessions.clone (); + public Map getSessions() { + return (Map) sessions.clone(); } - /** * Return a list of Helma nodes (HopObjects - the database object representing the user, * not the session object) representing currently logged in users. */ - public List getActiveUsers () { - ArrayList list = new ArrayList(); - // used to keep track of already added users - we only return - // one object per user, and users may have multiple sessions - HashSet usernames = new HashSet (); - for (Enumeration e=sessions.elements(); e.hasMoreElements(); ) { - Session s = (Session) e.nextElement (); - if(s==null) { - continue; - } else if (s.isLoggedIn() && !usernames.contains (s.getUID ()) ) { - // returns a session if it is logged in and has not been - // returned before (so for each logged-in user we get one - // session object, even if this user is logged in several - // times (used to retrieve the active users list). - INode node = s.getUserNode (); - // we check again because user may have been logged out between the first check - if (node != null) { - usernames.add (s.getUID ()); - list.add(node); - } - } - } - return list; - } + public List getActiveUsers() { + ArrayList list = new ArrayList(); + // used to keep track of already added users - we only return + // one object per user, and users may have multiple sessions + HashSet usernames = new HashSet(); + + for (Enumeration e = sessions.elements(); e.hasMoreElements();) { + Session s = (Session) e.nextElement(); + + if (s == null) { + continue; + } else if (s.isLoggedIn() && !usernames.contains(s.getUID())) { + // returns a session if it is logged in and has not been + // returned before (so for each logged-in user we get one + // session object, even if this user is logged in several + // times (used to retrieve the active users list). + INode node = s.getUserNode(); + + // we check again because user may have been logged out between the first check + if (node != null) { + usernames.add(s.getUID()); + list.add(node); + } + } + } + + return list; + } /** * Return a list of Helma nodes (HopObjects - the database object representing the user, * not the session object) representing registered users of this application. */ - public List getRegisteredUsers () { - ArrayList list = new ArrayList (); - INode users = getUserRoot (); - // first try to get them from subnodes (db) - for (Enumeration e=users.getSubnodes(); e.hasMoreElements(); ) { - list.add ((INode)e.nextElement ()); - } - // if none, try to get them from properties (internal db) - if (list.size()==0) { - for (Enumeration e=users.properties(); e.hasMoreElements(); ) { - list.add (users.getNode ((String)e.nextElement ())); - } - } - return list; - } + public List getRegisteredUsers() { + ArrayList list = new ArrayList(); + INode users = getUserRoot(); + // first try to get them from subnodes (db) + for (Enumeration e = users.getSubnodes(); e.hasMoreElements();) { + list.add((INode) e.nextElement()); + } + + // if none, try to get them from properties (internal db) + if (list.size() == 0) { + for (Enumeration e = users.properties(); e.hasMoreElements();) { + list.add(users.getNode((String) e.nextElement())); + } + } + + return list; + } /** * Return an array of SessionBean objects currently associated with a given * Helma user. */ - public List getSessionsForUsername (String username) { - ArrayList list = new ArrayList(); - if (username == null) - return list; - for (Enumeration e=sessions.elements(); e.hasMoreElements(); ) { - Session s = (Session) e.nextElement (); - if(s==null) { - continue; - } else if (username.equals (s.getUID ())) { - // append to list if session is logged in and fits the given username - list.add (new SessionBean (s)); - } - } - return list; - } + public List getSessionsForUsername(String username) { + ArrayList list = new ArrayList(); + if (username == null) { + return list; + } + + for (Enumeration e = sessions.elements(); e.hasMoreElements();) { + Session s = (Session) e.nextElement(); + + if (s == null) { + continue; + } else if (username.equals(s.getUID())) { + // append to list if session is logged in and fits the given username + list.add(new SessionBean(s)); + } + } + + return list; + } /** * Return the session currently associated with a given Hop session ID. */ - public Session getSession (String sessionID) { - if (sessionID == null) - return null; - return (Session) sessions.get (sessionID); - } + public Session getSession(String sessionID) { + if (sessionID == null) { + return null; + } + + return (Session) sessions.get(sessionID); + } /** * Register a user with the given user name and password. */ - public INode registerUser (String uname, String password) { - if (uname == null) - return null; - uname = uname.toLowerCase ().trim (); - if ("".equals (uname)) - return null; - INode unode = null; - try { - INode users = getUserRoot (); - unode = users.getNode (uname); - if (unode != null) - return null; + public INode registerUser(String uname, String password) { + if (uname == null) { + return null; + } - unode = users.createNode (uname); - unode.setPrototype ("user"); - unode.setDbMapping (userMapping); - String usernameField = userMapping.getNameField (); - String usernameProp = null; - if (usernameField != null) - usernameProp = userMapping.columnNameToProperty (usernameField); - if (usernameProp == null) - usernameProp = "name"; - unode.setName (uname); - unode.setString (usernameProp, uname); - unode.setString ("password", password); - return unode; - } catch (Exception x) { - logEvent ("Error registering User: "+x); - return null; - } + uname = uname.toLowerCase().trim(); + + if ("".equals(uname)) { + return null; + } + + INode unode = null; + + try { + INode users = getUserRoot(); + + unode = users.getNode(uname); + + if (unode != null) { + return null; + } + + unode = users.createNode(uname); + unode.setPrototype("user"); + unode.setDbMapping(userMapping); + + String usernameField = userMapping.getNameField(); + String usernameProp = null; + + if (usernameField != null) { + usernameProp = userMapping.columnNameToProperty(usernameField); + } + + if (usernameProp == null) { + usernameProp = "name"; + } + + unode.setName(uname); + unode.setString(usernameProp, uname); + unode.setString("password", password); + + return unode; + } catch (Exception x) { + logEvent("Error registering User: " + x); + + return null; + } } /** * Log in a user given his or her user name and password. */ - public boolean loginSession (String uname, String password, Session session) { - // Check the name/password of a user and log it in to the current session - if (uname == null) - return false; - uname = uname.toLowerCase ().trim (); - if ("".equals (uname)) - return false; - try { - INode users = getUserRoot (); - Node unode = (Node) users.getNode (uname); - String pw = unode.getString ("password"); - if (pw != null && pw.equals (password)) { - // let the old user-object forget about this session - logoutSession(session); - session.login (unode); - return true; - } - } catch (Exception x) { - return false; - } - return false; + public boolean loginSession(String uname, String password, Session session) { + // Check the name/password of a user and log it in to the current session + if (uname == null) { + return false; + } + + uname = uname.toLowerCase().trim(); + + if ("".equals(uname)) { + return false; + } + + try { + INode users = getUserRoot(); + Node unode = (Node) users.getNode(uname); + String pw = unode.getString("password"); + + if ((pw != null) && pw.equals(password)) { + // let the old user-object forget about this session + logoutSession(session); + session.login(unode); + + return true; + } + } catch (Exception x) { + return false; + } + + return false; } /** * Log out a session from this application. */ - public void logoutSession (Session session) { - session.logout(); + public void logoutSession(Session session) { + session.logout(); } /** @@ -833,118 +983,146 @@ public final class Application implements IPathElement, Runnable { * authenticated against a passwd file in the application directory. This is to have some sort of * authentication available *before* the application is up and running, i.e. for application setup tasks. */ - public boolean authenticate (String uname, String password) { - if (uname == null || password == null) - return false; - return pwfile.authenticate (uname, password); + public boolean authenticate(String uname, String password) { + if ((uname == null) || (password == null)) { + return false; + } + + return pwfile.authenticate(uname, password); } /** * Return the href to the root of this application. */ - public String getRootHref () { - return getNodeHref (getDataRoot(), null); + public String getRootHref() { + return getNodeHref(getDataRoot(), null); } - /** * Return a path to be used in a URL pointing to the given element and action */ - public String getNodeHref (Object elem, String actionName) { - // Object root = getDataRoot (); - // check optional root prototype from app.properties - String rootProto = props.getProperty ("rootPrototype"); + public String getNodeHref(Object elem, String actionName) { + // Object root = getDataRoot (); + // check optional root prototype from app.properties + String rootProto = props.getProperty("rootPrototype"); - StringBuffer b = new StringBuffer (baseURI); + StringBuffer b = new StringBuffer(baseURI); - composeHref (elem, b, rootProto, 0); + composeHref(elem, b, rootProto, 0); - if (actionName != null) - b.append (UrlEncoded.encode (actionName)); + if (actionName != null) { + b.append(UrlEncoded.encode(actionName)); + } - return b.toString (); + return b.toString(); } - private final void composeHref (Object elem, StringBuffer b, String rootProto, int pathCount) { - if (elem == null || pathCount > 20) - return; - if (rootProto != null && rootProto.equals (getPrototypeName (elem))) - return; - Object parent = getParentElement (elem); - if (parent == null) - return; - composeHref (getParentElement (elem), b, rootProto, pathCount++); - b.append (UrlEncoded.encode (getElementName (elem))); - b.append ("/"); + private final void composeHref(Object elem, StringBuffer b, String rootProto, + int pathCount) { + if ((elem == null) || (pathCount > 20)) { + return; + } + + if ((rootProto != null) && rootProto.equals(getPrototypeName(elem))) { + return; + } + + Object parent = getParentElement(elem); + + if (parent == null) { + return; + } + + composeHref(getParentElement(elem), b, rootProto, pathCount++); + b.append(UrlEncoded.encode(getElementName(elem))); + b.append("/"); } /** * This method sets the base URL of this application which will be prepended to * the actual object path. */ - public void setBaseURI (String uri) { - if (uri == null) - this.baseURI = "/"; - else if (!uri.endsWith ("/")) - this.baseURI = uri+"/"; - else - this.baseURI = uri; + public void setBaseURI(String uri) { + if (uri == null) { + this.baseURI = "/"; + } else if (!uri.endsWith("/")) { + this.baseURI = uri + "/"; + } else { + this.baseURI = uri; + } } /** - * Return true if the baseURI property is defined in the application + * Return true if the baseURI property is defined in the application * properties, false otherwise. */ - public boolean hasExplicitBaseURI () { - return props.containsKey ("baseuri"); + public boolean hasExplicitBaseURI() { + return props.containsKey("baseuri"); } /** * Tell other classes whether they should output logging information for this application. */ - public boolean debug () { - return debug; + public boolean debug() { + return debug; } - public RequestEvaluator getCurrentRequestEvaluator () { - Thread thread = Thread.currentThread (); - int l = allThreads.size(); - for (int i=0; i cleanupSleep) try { - lastCleanup = now; - Hashtable cloned = (Hashtable) sessions.clone (); - for (Enumeration e = cloned.elements (); e.hasMoreElements (); ) { - Session session = (Session) e.nextElement (); - if (now - session.lastTouched () > sessionTimeout * 60000) { - NodeHandle userhandle = session.userHandle; - if (userhandle != null) { - try { - Object[] param = { session.getSessionID() }; - eval.invokeFunction (userhandle, "onLogout", param); - } catch (Exception ignore) {} - } - destroySession(session); - } - } - } catch (Exception cx) { - logEvent ("Error cleaning up sessions: "+cx); - cx.printStackTrace (); - } + // check if we should clean up user sessions + if ((now - lastCleanup) > cleanupSleep) { + try { + lastCleanup = now; - // check if we should call scheduler - if (now - lastScheduler > scheduleSleep) { - lastScheduler = now; - Object val = null; - try { - val = eval.invokeFunction ((INode) null, "scheduler", new Object[0]); - } catch (Exception ignore) {} - try { - int ret = ((Number) val).intValue (); - if (ret < 1000) - scheduleSleep = 60000l; - else - scheduleSleep = ret; - } catch (Exception ignore) {} - // logEvent ("Called scheduler for "+name+", will sleep for "+scheduleSleep+" millis"); - } + Hashtable cloned = (Hashtable) sessions.clone(); - // sleep until we have work to do - try { - worker.sleep (Math.min (cleanupSleep, scheduleSleep)); - } catch (InterruptedException x) { - logEvent ("Scheduler for "+name+" interrupted"); - worker = null; - break; - } - } - logEvent ("Scheduler for "+name+" exiting"); + for (Enumeration e = cloned.elements(); e.hasMoreElements();) { + Session session = (Session) e.nextElement(); + + if ((now - session.lastTouched()) > (sessionTimeout * 60000)) { + NodeHandle userhandle = session.userHandle; + + if (userhandle != null) { + try { + Object[] param = { session.getSessionID() }; + + eval.invokeFunction(userhandle, "onLogout", param); + } catch (Exception ignore) { + } + } + + destroySession(session); + } + } + } catch (Exception cx) { + logEvent("Error cleaning up sessions: " + cx); + cx.printStackTrace(); + } + } + + if ((cronJobs == null) || (props.lastModified() > lastPropertyRead)) { + updateProperties(); + cronJobs = CronJob.parse(props); + } + + Date d = new Date(); + List jobs = (List) cronJobs.clone(); + + jobs.addAll(customCronJobs.values()); + CronJob.sort(jobs); + + for (Iterator i = jobs.iterator(); i.hasNext();) { + CronJob j = (CronJob) i.next(); + + if (j.appliesToDate(d)) { + // check if the job is already active ... + if (activeCronJobs.containsKey(j.getName())) { + logEvent(j + " is still active, skipped in this minute"); + + continue; + } + + RequestEvaluator thisEvaluator; + + try { + thisEvaluator = getEvaluator(); + } catch (RuntimeException rt) { + if (stopped == false) { + logEvent("couldn't execute " + j + + ", maximum thread count reached"); + + continue; + } else { + break; + } + } + + // if the job has a long timeout or we're already late during this minute + // the job is run from an extra thread + if ((j.getTimeout() > 20000) || + (CronJob.millisToNextFullMinute() < 30000)) { + CronRunner r = new CronRunner(thisEvaluator, j); + + r.start(); + activeCronJobs.put(j.getName(), r); + } else { + try { + thisEvaluator.invokeFunction((INode) null, j.getFunction(), + new Object[0], j.getTimeout()); + } catch (Exception ex) { + logEvent("error running " + j + ": " + ex.toString()); + } finally { + if (stopped == false) { + releaseEvaluator(thisEvaluator); + } + } + } + + thisEvaluator = null; + } + } + + // sleep until the next full minute + try { + worker.sleep(CronJob.millisToNextFullMinute()); + } catch (InterruptedException x) { + logEvent("Scheduler for " + name + " interrupted"); + worker = null; + break; + } + } + + // when interrupted, shutdown running cron jobs + synchronized (activeCronJobs) { + for (Iterator i = activeCronJobs.keySet().iterator(); i.hasNext();) { + String jobname = (String) i.next(); + + ((CronRunner) activeCronJobs.get(jobname)).interrupt(); + activeCronJobs.remove(jobname); + } + } + + logEvent("Scheduler for " + name + " exiting"); } - /** * Check whether a prototype is for scripting a java class, i.e. if there's an entry * for it in the class.properties file. */ - public boolean isJavaPrototype (String typename) { - for (Enumeration en = classMapping.elements(); en.hasMoreElements(); ) { - String value = (String) en.nextElement (); - if (typename.equals (value)) - return true; - } - return false; + public boolean isJavaPrototype(String typename) { + for (Enumeration en = classMapping.elements(); en.hasMoreElements();) { + String value = (String) en.nextElement(); + + if (typename.equals(value)) { + return true; + } + } + + return false; } /** * Return a DbSource object for a given name. A DbSource is a relational database defined * in a db.properties file. */ - public DbSource getDbSource (String name) { - String dbSrcName = name.toLowerCase (); - DbSource dbs = (DbSource) dbSources.get (dbSrcName); - if (dbs != null) - return dbs; - if (dbProps.getProperty (dbSrcName+".url") != null && dbProps.getProperty (dbSrcName+".driver") != null) { - try { - dbs = new DbSource (name, dbProps); - dbSources.put (dbSrcName, dbs); - } catch (Exception problem) { - logEvent ("Error creating DbSource "+name); - logEvent ("Reason: "+problem); - } - } - return dbs; + public DbSource getDbSource(String name) { + String dbSrcName = name.toLowerCase(); + DbSource dbs = (DbSource) dbSources.get(dbSrcName); + + if (dbs != null) { + return dbs; + } + + if ((dbProps.getProperty(dbSrcName + ".url") != null) && + (dbProps.getProperty(dbSrcName + ".driver") != null)) { + try { + dbs = new DbSource(name, dbProps); + dbSources.put(dbSrcName, dbs); + } catch (Exception problem) { + logEvent("Error creating DbSource " + name); + logEvent("Reason: " + problem); + } + } + + return dbs; } /** * Return the name of this application */ - public String getName () { - return name; + public String getName() { + return name; } /** * Return the directory of this application */ public File getAppDir() { - return appDir; + return appDir; } /** * Return the directory of the Helma server */ public File getServerDir() { - return home; + return home; } - /** * Get the DbMapping associated with a prototype name in this application */ - public DbMapping getDbMapping (String typename) { - Prototype proto = typemgr.getPrototype (typename); - if (proto == null) - return null; - return proto.getDbMapping (); + public DbMapping getDbMapping(String typename) { + Prototype proto = typemgr.getPrototype(typename); + + if (proto == null) { + return null; + } + + return proto.getDbMapping(); } + private synchronized void updateProperties() { + // if so property file has been updated, re-read props. + if (props.lastModified() > lastPropertyRead) { + // character encoding to be used for responses + charset = props.getProperty("charset", "ISO-8859-1"); - private synchronized void updateProperties () { - // if so property file has been updated, re-read props. - if (props.lastModified () > lastPropertyRead) { - // character encoding to be used for responses - charset = props.getProperty ("charset", "ISO-8859-1"); - // debug flag - debug = "true".equalsIgnoreCase (props.getProperty ("debug")); - try { - requestTimeout = Long.parseLong (props.getProperty ("requestTimeout", "60"))*1000l; - } catch (Exception ignore) { - // go with default value - requestTimeout = 60000l; - } - // set base URI - String base = props.getProperty ("baseuri"); - if (base != null) - setBaseURI (base); - else if (baseURI == null) - baseURI = "/"; - // update the XML-RPC access list, containting prototype.method - // entries of functions that may be called via XML-RPC - String xmlrpcAccessProp = props.getProperty ("xmlrpcaccess"); - HashSet xra = new HashSet (); - if (xmlrpcAccessProp != null) { - StringTokenizer st = new StringTokenizer (xmlrpcAccessProp, ",; "); - while (st.hasMoreTokens ()) { - String token = st.nextToken ().trim (); - xra.add (token.toLowerCase()); - } - } - xmlrpcAccess = xra; - // if node manager exists, update it - if (nmgr != null) - nmgr.updateProperties (props); - // update extensions - Vector extensions = Server.getServer ().getExtensions (); - for (int i=0; i lastSkinmapLoad) - load (); - } + return super.isEmpty(); + } - private synchronized void load () { - if (lastUpdate == lastSkinmapLoad) - return; - super.clear (); - // System.err.println ("LOADING SKIN VALUES: "+Prototype.this); - for (Iterator i = skins.entrySet().iterator(); i.hasNext(); ) { - Map.Entry e = (Map.Entry) i.next (); - super.put (e.getKey(), e.getValue()); - } - // if skinpath is not null, overload/add skins from there - if (skinpath != null) { - for (int i=skinpath.length-1; i>=0; i--) { - if (skinpath[i] != null && skinpath[i] instanceof String) { - Map m = app.skinmgr.getSkinFiles ((String) skinpath[i], Prototype.this); - if (m != null) - super.putAll (m); - } - } - } - lastSkinmapLoad = lastUpdate; - } - - public String toString () { - return "[SkinMap "+name+"]"; - } + public Set keySet() { + checkForUpdates(); + return super.keySet(); + } + + public Object put(Object key, Object value) { + // checkForUpdates (); + return super.put(key, value); + } + + public void putAll(Map t) { + // checkForUpdates (); + super.putAll(t); + } + + public Object remove(Object key) { + checkForUpdates(); + + return super.remove(key); + } + + public int size() { + checkForUpdates(); + + return super.size(); + } + + public Collection values() { + checkForUpdates(); + + return super.values(); + } + + private void checkForUpdates() { + if ( /* lastCheck < System.currentTimeMillis()- 2000l*/ + !isUpToDate()) { + app.typemgr.updatePrototype(Prototype.this); + } + + if (lastUpdate > lastSkinmapLoad) { + load(); + } + } + + private synchronized void load() { + if (lastUpdate == lastSkinmapLoad) { + return; + } + + super.clear(); + + // System.err.println ("LOADING SKIN VALUES: "+Prototype.this); + for (Iterator i = skins.entrySet().iterator(); i.hasNext();) { + Map.Entry e = (Map.Entry) i.next(); + + super.put(e.getKey(), e.getValue()); + } + + // if skinpath is not null, overload/add skins from there + if (skinpath != null) { + for (int i = skinpath.length - 1; i >= 0; i--) { + if ((skinpath[i] != null) && skinpath[i] instanceof String) { + Map m = app.skinmgr.getSkinFiles((String) skinpath[i], + Prototype.this); + + if (m != null) { + super.putAll(m); + } + } + } + } + + lastSkinmapLoad = lastUpdate; + } + + public String toString() { + return "[SkinMap " + name + "]"; + } } - } - diff --git a/src/helma/framework/core/RemoteApplication.java b/src/helma/framework/core/RemoteApplication.java index f854b4d0..10a7ec6d 100644 --- a/src/helma/framework/core/RemoteApplication.java +++ b/src/helma/framework/core/RemoteApplication.java @@ -1,5 +1,18 @@ -// RemoteApplication.java -// Copyright (c) Hannes Wallnöfer 2002 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework.core; @@ -12,43 +25,45 @@ import java.util.Vector; /** * Proxy class for Aplication that listens to requests via RMI. */ - -public class RemoteApplication - extends UnicastRemoteObject - implements IRemoteApp, IReplicationListener { - +public class RemoteApplication extends UnicastRemoteObject implements IRemoteApp, + IReplicationListener { Application app; - - public RemoteApplication (Application app) throws RemoteException { - this.app = app; + /** + * Creates a new RemoteApplication object. + * + * @param app ... + * + * @throws RemoteException ... + */ + public RemoteApplication(Application app) throws RemoteException { + this.app = app; } - /** * ping method to let clients know if the server is reachable */ - public void ping () { - // do nothing + public void ping() { + // do nothing } /** * Execute a request coming in from a web client. */ - public ResponseTrans execute (RequestTrans req) { - return app.execute (req); + public ResponseTrans execute(RequestTrans req) { + return app.execute(req); } - /** * Update HopObjects in this application's cache. This is used to replicate * application caches in a distributed app environment */ - public void replicateCache (Vector add, Vector delete) { - if (!"true".equalsIgnoreCase (app.getProperty ("allowReplication"))) { - app.logEvent ("Rejecting cache replication event: allowReplication property is not set to true"); - throw new RuntimeException ("Replication event rejected: setup does not allow replication."); - } - app.nmgr.replicateCache (add, delete); + public void replicateCache(Vector add, Vector delete) { + if (!"true".equalsIgnoreCase(app.getProperty("allowReplication"))) { + app.logEvent("Rejecting cache replication event: allowReplication property is not set to true"); + throw new RuntimeException("Replication event rejected: setup does not allow replication."); + } + + app.nmgr.replicateCache(add, delete); } } diff --git a/src/helma/framework/core/RequestEvaluator.java b/src/helma/framework/core/RequestEvaluator.java index e9f31ae4..029d599d 100644 --- a/src/helma/framework/core/RequestEvaluator.java +++ b/src/helma/framework/core/RequestEvaluator.java @@ -1,15 +1,28 @@ -// RequestEvaluator.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.framework.core; +import helma.framework.*; import helma.objectmodel.*; import helma.objectmodel.db.*; -import helma.framework.*; import helma.scripting.*; import helma.util.*; -import java.util.*; import java.lang.reflect.*; +import java.util.*; /** * This class does the work for incoming requests. It holds a transactor thread @@ -18,17 +31,15 @@ import java.lang.reflect.*; * specified by the application has passed. In the latter case, the evaluator thread * is killed and an error message is returned. */ - public final class RequestEvaluator implements Runnable { - - + static final int NONE = 0; // no request + static final int HTTP = 1; // via HTTP gateway + static final int XMLRPC = 2; // via XML-RPC + static final int INTERNAL = 3; // generic function call, e.g. by scheduler public final Application app; - protected ScriptingEngine scriptingEngine; - public RequestTrans req; public ResponseTrans res; - volatile Transactor rtx; // the object on which to invoke a function, if specified @@ -54,631 +65,815 @@ public final class RequestEvaluator implements Runnable { // the type of request to be serviced int reqtype; - static final int NONE = 0; // no request - static final int HTTP = 1; // via HTTP gateway - static final int XMLRPC = 2; // via XML-RPC - static final int INTERNAL = 3; // generic function call, e.g. by scheduler - protected int skinDepth; /** * Create a new RequestEvaluator for this application. */ - public RequestEvaluator (Application app) { - this.app = app; + public RequestEvaluator(Application app) { + this.app = app; } - protected void initScriptingEngine () { - if (scriptingEngine == null) { - String engineClassName = app.getProperty ( - "scriptingEngine", - "helma.scripting.fesi.PhantomEngine"); - try { - Class clazz = app.getClassLoader().loadClass (engineClassName); - scriptingEngine = (ScriptingEngine) clazz.newInstance (); - scriptingEngine.init (app, this); - } catch (Exception x) { - Throwable t = x; - if (x instanceof InvocationTargetException) - t = ((InvocationTargetException) x).getTargetException (); - app.logEvent ("******************************************"); - app.logEvent ("*** Error creating scripting engine: "); - app.logEvent ("*** "+t.toString()); - app.logEvent ("******************************************"); - } - } + protected void initScriptingEngine() { + if (scriptingEngine == null) { + String engineClassName = app.getProperty("scriptingEngine", + "helma.scripting.fesi.PhantomEngine"); + + try { + Class clazz = app.getClassLoader().loadClass(engineClassName); + + scriptingEngine = (ScriptingEngine) clazz.newInstance(); + scriptingEngine.init(app, this); + } catch (Exception x) { + Throwable t = x; + + if (x instanceof InvocationTargetException) { + t = ((InvocationTargetException) x).getTargetException(); + } + + app.logEvent("******************************************"); + app.logEvent("*** Error creating scripting engine: "); + app.logEvent("*** " + t.toString()); + app.logEvent("******************************************"); + } + } } - public void run () { - + /** + * + */ + public void run() { int txcount = 0; + // first, set a local variable to the current transactor thread so we know // when it's time to quit because another thread took over. - Transactor localrtx = (Transactor) Thread.currentThread (); + Transactor localrtx = (Transactor) Thread.currentThread(); try { - do { - - // long startCheck = System.currentTimeMillis (); - app.typemgr.checkPrototypes (); - initScriptingEngine (); - scriptingEngine.updatePrototypes (); - // System.err.println ("Type check overhead: "+(System.currentTimeMillis ()-startCheck)+" millis"); - - // object refs to ressolve request path - Object root, currentElement; - - requestPath = new ArrayList (); - - switch (reqtype) { - case HTTP: - int tries = 0; - boolean done = false; - String error = null; - - while (!done) { - - currentElement = null; - - try { - - // used for logging - StringBuffer txname = new StringBuffer(app.getName()); - txname.append ("/"); - txname.append (error == null ? req.path : "error"); - // begin transaction - localrtx.begin (txname.toString()); - - String action = null; - - root = app.getDataRoot (); - - HashMap globals = new HashMap (); - globals.put ("root", root); - globals.put ("session", new SessionBean (session)); - globals.put ("req", new RequestBean (req)); - globals.put ("res", new ResponseBean (res)); - globals.put ("app", new ApplicationBean (app)); - globals.put ("path", requestPath); - req.startTime = System.currentTimeMillis (); - if (error != null) - res.error = error; - if (session.message != null) { - // bring over the message from a redirect - res.message = session.message; - session.message = null; - } - Map macroHandlers = res.getMacroHandlers (); - - try { - - if (error != null) { - // there was an error in the previous loop, call error handler - currentElement = root; - // do not reset the requestPath so error handler can use the original one - // get error handler action - String errorAction = app.props.getProperty ("error", "error"); - action = getAction (currentElement, errorAction); - if (action == null) - throw new RuntimeException (error); - - } else if (req.path == null || "".equals (req.path.trim ())) { - currentElement = root; - requestPath.add (currentElement); - macroHandlers.put ("root", root); - action = getAction (currentElement, null); - if (action == null) - throw new FrameworkException ("Action not found"); - - } else { - - // march down request path... - StringTokenizer st = new StringTokenizer (req.path, "/"); - int ntokens = st.countTokens (); - // limit path to < 50 tokens - if (ntokens > 50) - throw new RuntimeException ("Path too long"); - String[] pathItems = new String [ntokens]; - for (int i=0; iError in application '"+app.getName()+"':

"+error+"
"); - done = true; - } - } - - } catch (Exception x) { - - abortTransaction (false); - - // If the transactor thread has been killed by the invoker thread we don't have to - // bother for the error message, just quit. - if (localrtx != rtx) { - break; - } - - res.reset (); - - // check if we tried to process the error already - if (error == null) { - app.errorCount += 1; - app.logEvent ("Exception in "+Thread.currentThread()+": "+x); - // set done to false so that the error will be processed - done = false; - error = x.getMessage (); - if (error == null || error.length() == 0) - error = x.toString (); - if (error == null) - error = "Unspecified error"; - } else { - // error in error action. use traditional minimal error message - res.write ("Error in application '"+app.getName()+"':

"+error+"
"); - done = true; - } - - } - } - break; - case XMLRPC: - try { - localrtx.begin (app.getName()+":xmlrpc:"+method); - - root = app.getDataRoot (); - - HashMap globals = new HashMap (); - globals.put ("root", root); - globals.put ("res", new ResponseBean (res)); - globals.put ("app", new ApplicationBean (app)); - - scriptingEngine.enterContext (globals); - - currentElement = root; - - if (method.indexOf (".") > -1) { - StringTokenizer st = new StringTokenizer (method, "."); - int cnt = st.countTokens (); - for (int i=1; i 50) { + throw new RuntimeException("Path too long"); + } + + String[] pathItems = new String[ntokens]; + + for (int i = 0; i < ntokens; i++) + pathItems[i] = st.nextToken(); + + currentElement = root; + requestPath.add(currentElement); + macroHandlers.put("root", root); + + for (int i = 0; i < ntokens; i++) { + if (currentElement == null) { + throw new FrameworkException("Object not found."); + } + + if (pathItems[i].length() == 0) { + continue; + } + + // we used to do special processing for /user and /users + // here but with the framework cleanup, this stuff has to be + // mounted manually. + // if we're at the last element of the path, + // try to interpret it as action name. + if (i == (ntokens - 1)) { + action = getAction(currentElement, + pathItems[i]); + } + + if (action == null) { + currentElement = app.getChildElement(currentElement, + pathItems[i]); + + // add object to request path if suitable + if (currentElement != null) { + // add to requestPath array + requestPath.add(currentElement); + + Prototype proto = app.getPrototype(currentElement); + + if (proto != null) { + proto.addToHandlerMap(macroHandlers, + currentElement); + } + } + } + } + + if (currentElement == null) { + throw new FrameworkException("Object not found."); + } + + if (action == null) { + action = getAction(currentElement, null); + } + + if (action == null) { + throw new FrameworkException("Action not found"); + } + } + } catch (FrameworkException notfound) { + if (error != null) { + + // we already have an error and the error template wasn't found, + // display it instead of notfound message + throw new RuntimeException(); + } + + // The path could not be resolved. Check if there is a "not found" action + // specified in the property file. + res.status = 404; + + String notFoundAction = app.props.getProperty("notFound", + "notfound"); + + currentElement = root; + action = getAction(currentElement, notFoundAction); + + if (action == null) { + throw new FrameworkException(notfound.getMessage()); + } + } + + ///////////////////////////////////////////////////////////////////////////// + // end of path resolution section + ///////////////////////////////////////////////////////////////////////////// + // beginning of execution section + try { + // enter execution context + scriptingEngine.enterContext(globals); + + // set the req.action property, cutting off the _action suffix + req.action = action.substring(0, action.length() - 7); + + // set the application checksum in response to make ETag + // generation sensitive to changes in the app + res.setApplicationChecksum(app.getChecksum()); + + // reset skin recursion detection counter + skinDepth = 0; + + // try calling onRequest() function on object before + // calling the actual action + try { + if (scriptingEngine.hasFunction(currentElement, + "onRequest")) { + scriptingEngine.invoke(currentElement, + "onRequest", + new Object[0], false); + } + } catch (RedirectException redir) { + throw redir; + } + + // reset skin recursion detection counter + skinDepth = 0; + + // do the actual action invocation + scriptingEngine.invoke(currentElement, action, + new Object[0], false); + } catch (RedirectException redirect) { + // res.redirect = redirect.getMessage (); + // if there is a message set, save it on the user object for the next request + if (res.message != null) { + session.message = res.message; + } + + done = true; + } + + // check if we're still the one and only or if the waiting thread has given up on us already + commitTransaction(); + done = true; + } catch (ConcurrencyException x) { + res.reset(); + + if (++tries < 8) { + // try again after waiting some period + abortTransaction(true); + + try { + // wait a bit longer with each try + int base = 800 * tries; + + Thread.currentThread().sleep((long) (base + + (Math.random() * base * 2))); + } catch (Exception ignore) { + } + + continue; + } else { + abortTransaction(false); + + if (error == null) { + app.errorCount += 1; + + // set done to false so that the error will be processed + done = false; + error = "Couldn't complete transaction due to heavy object traffic (tried " + + tries + " times)"; + } else { + // error in error action. use traditional minimal error message + res.write("Error in application '" + + app.getName() + "':

" +
+                                                  error + "
"); + done = true; + } + } + } catch (Exception x) { + abortTransaction(false); + + // If the transactor thread has been killed by the invoker thread we don't have to + // bother for the error message, just quit. + if (localrtx != rtx) { + break; + } + + res.reset(); + + // check if we tried to process the error already + if (error == null) { + app.errorCount += 1; + app.logEvent("Exception in " + + Thread.currentThread() + ": " + x); + + // set done to false so that the error will be processed + done = false; + error = x.getMessage(); + + if ((error == null) || (error.length() == 0)) { + error = x.toString(); + } + + if (error == null) { + error = "Unspecified error"; + } + } else { + // error in error action. use traditional minimal error message + res.write("Error in application '" + + app.getName() + "':

" +
+                                              error + "
"); + done = true; + } + } + } + + break; + + case XMLRPC: + + try { + localrtx.begin(app.getName() + ":xmlrpc:" + method); - // avoid going into transaction if called function doesn't exist - boolean functionexists = true; - functionexists = scriptingEngine.hasFunction (thisObject, method); - - if (!functionexists) { - // function doesn't exist, nothing to do here. - reqtype = NONE; - } else try { - localrtx.begin (funcdesc); - - root = app.getDataRoot (); - - HashMap globals = new HashMap (); - globals.put ("root", root); - globals.put ("res", new ResponseBean (res)); - globals.put ("app", new ApplicationBean (app)); - - scriptingEngine.enterContext (globals); - // reset skin recursion detection counter - skinDepth = 0; + root = app.getDataRoot(); - result = scriptingEngine.invoke (thisObject, method, args, false); - commitTransaction (); + HashMap globals = new HashMap(); - } catch (Exception x) { + globals.put("root", root); + globals.put("res", new ResponseBean(res)); + globals.put("app", new ApplicationBean(app)); - abortTransaction (false); + scriptingEngine.enterContext(globals); - app.logEvent ("Exception in "+Thread.currentThread()+": "+x); + currentElement = root; - // If the transactor thread has been killed by the invoker thread we don't have to - // bother for the error message, just quit. - if (localrtx != rtx) { - return; - } + if (method.indexOf(".") > -1) { + StringTokenizer st = new StringTokenizer(method, "."); + int cnt = st.countTokens(); - this.exception = x; - } + for (int i = 1; i < cnt; i++) { + String next = st.nextToken(); - break; + currentElement = app.getChildElement(currentElement, + next); + } - } + if (currentElement == null) { + throw new FrameworkException("Method name \"" + + method + + "\" could not be resolved."); + } - // exit execution context - scriptingEngine.exitContext (); + method = st.nextToken(); + } - // make sure there is only one thread running per instance of this class - // if localrtx != rtx, the current thread has been aborted and there's no need to notify - if (localrtx != rtx) { - localrtx.closeConnections (); - return; - } + // check XML-RPC access permissions + String proto = app.getPrototypeName(currentElement); - notifyAndWait (); + app.checkXmlRpcAccess(proto, method); - } while (localrtx == rtx); + // reset skin recursion detection counter + skinDepth = 0; + result = scriptingEngine.invoke(currentElement, method, args, + true); + commitTransaction(); + } catch (Exception x) { + abortTransaction(false); + + app.logEvent("Exception in " + Thread.currentThread() + ": " + + x); + + // If the transactor thread has been killed by the invoker thread we don't have to + // bother for the error message, just quit. + if (localrtx != rtx) { + return; + } + + this.exception = x; + } + + break; + + case INTERNAL: + + // Just a human readable descriptor of this invocation + String funcdesc = app.getName() + ":internal:" + method; + + // if thisObject is an instance of NodeHandle, get the node object itself. + if ((thisObject != null) && thisObject instanceof NodeHandle) { + thisObject = ((NodeHandle) thisObject).getNode(app.nmgr.safe); + + // see if a valid node was returned + if (thisObject == null) { + reqtype = NONE; + + break; + } + } + + // avoid going into transaction if called function doesn't exist + boolean functionexists = true; + + functionexists = scriptingEngine.hasFunction(thisObject, method); + + if (!functionexists) { + // function doesn't exist, nothing to do here. + reqtype = NONE; + } else { + try { + localrtx.begin(funcdesc); + + root = app.getDataRoot(); + + HashMap globals = new HashMap(); + + globals.put("root", root); + globals.put("res", new ResponseBean(res)); + globals.put("app", new ApplicationBean(app)); + + scriptingEngine.enterContext(globals); + + // reset skin recursion detection counter + skinDepth = 0; + + result = scriptingEngine.invoke(thisObject, method, args, + false); + commitTransaction(); + } catch (Exception x) { + abortTransaction(false); + + app.logEvent("Exception in " + Thread.currentThread() + + ": " + x); + + // If the transactor thread has been killed by the invoker thread we don't have to + // bother for the error message, just quit. + if (localrtx != rtx) { + return; + } + + this.exception = x; + } + } + + break; + } + + // exit execution context + scriptingEngine.exitContext(); + + // make sure there is only one thread running per instance of this class + // if localrtx != rtx, the current thread has been aborted and there's no need to notify + if (localrtx != rtx) { + localrtx.closeConnections(); + + return; + } + + notifyAndWait(); + } while (localrtx == rtx); } finally { - localrtx.closeConnections (); + localrtx.closeConnections(); } } /** * Called by the transactor thread when it has successfully fulfilled a request. */ - synchronized void commitTransaction () throws Exception { - Transactor localrtx = (Transactor) Thread.currentThread (); - if (localrtx == rtx) { - reqtype = NONE; - localrtx.commit (); - } else { - throw new TimeoutException (); - } + synchronized void commitTransaction() throws Exception { + Transactor localrtx = (Transactor) Thread.currentThread(); + + if (localrtx == rtx) { + reqtype = NONE; + localrtx.commit(); + } else { + throw new TimeoutException(); + } } - synchronized void abortTransaction (boolean retry) { - Transactor localrtx = (Transactor) Thread.currentThread (); - if (!retry && localrtx == rtx) - reqtype = NONE; - try { - localrtx.abort (); - } catch (Exception ignore) {} + synchronized void abortTransaction(boolean retry) { + Transactor localrtx = (Transactor) Thread.currentThread(); + + if (!retry && (localrtx == rtx)) { + reqtype = NONE; + } + + try { + localrtx.abort(); + } catch (Exception ignore) { + } } /** * Tell waiting thread that we're done, then wait for next request */ - synchronized void notifyAndWait () { - Transactor localrtx = (Transactor) Thread.currentThread (); - if (reqtype != NONE) - return; // is there a new request already? + synchronized void notifyAndWait() { + Transactor localrtx = (Transactor) Thread.currentThread(); - notifyAll (); - try { - // wait for request, max 10 min - wait (1000*60*10); - // if no request arrived, release ressources and thread - if (reqtype == NONE && rtx == localrtx) { - // comment this in to release not just the thread, but also the scripting engine. - // currently we don't do this because of the risk of memory leaks (objects from - // framework referencing into the scripting engine) - // scriptingEngine = null; - rtx = null; - } - } catch (InterruptedException ir) {} + if (reqtype != NONE) { + return; // is there a new request already? + } + + notifyAll(); + + try { + // wait for request, max 10 min + wait(1000 * 60 * 10); + + // if no request arrived, release ressources and thread + if ((reqtype == NONE) && (rtx == localrtx)) { + // comment this in to release not just the thread, but also the scripting engine. + // currently we don't do this because of the risk of memory leaks (objects from + // framework referencing into the scripting engine) + // scriptingEngine = null; + rtx = null; + } + } catch (InterruptedException ir) { + } } - public synchronized ResponseTrans invoke (RequestTrans req, Session session) throws Exception { - this.reqtype = HTTP; - this.req = req; - this.session = session; - this.res = new ResponseTrans (req); + /** + * + * + * @param req ... + * @param session ... + * + * @return ... + * + * @throws Exception ... + */ + public synchronized ResponseTrans invoke(RequestTrans req, Session session) + throws Exception { + this.reqtype = HTTP; + this.req = req; + this.session = session; + this.res = new ResponseTrans(req); - app.activeRequests.put (req, this); + app.activeRequests.put(req, this); - checkThread (); - wait (app.requestTimeout); - if (reqtype != NONE) { - app.logEvent ("Stopping Thread for Request "+app.getName()+"/"+req.path); - stopThread (); - res.reset (); - res.write ("Error in application '" + - app.getName() + - "':

Request timed out.
"); - } - return res; + checkThread(); + wait(app.requestTimeout); + + if (reqtype != NONE) { + app.logEvent("Stopping Thread for Request " + app.getName() + "/" + req.path); + stopThread(); + res.reset(); + res.write("Error in application '" + app.getName() + + "':

Request timed out.
"); + } + + return res; } /** * This checks if the Evaluator is already executing an equal request. If so, attach to it and * wait for it to complete. Otherwise return null, so the application knows it has to run the request. */ - public synchronized ResponseTrans attachRequest (RequestTrans req) throws InterruptedException { - if (this.req == null || res == null || !this.req.equals (req)) - return null; - // we already know our response object - ResponseTrans r = res; - if (reqtype != NONE) - wait (app.requestTimeout); - return r; + public synchronized ResponseTrans attachRequest(RequestTrans req) + throws InterruptedException { + if ((this.req == null) || (res == null) || !this.req.equals(req)) { + return null; + } + + // we already know our response object + ResponseTrans r = res; + + if (reqtype != NONE) { + wait(app.requestTimeout); + } + + return r; } + /** + * + * + * @param method ... + * @param args ... + * + * @return ... + * + * @throws Exception ... + */ + public synchronized Object invokeXmlRpc(String method, Object[] args) + throws Exception { + this.reqtype = XMLRPC; + this.session = null; + this.method = method; + this.args = args; + this.res = new ResponseTrans(); + result = null; + exception = null; - public synchronized Object invokeXmlRpc (String method, Object[] args) throws Exception { - this.reqtype = XMLRPC; - this.session = null; - this.method = method; - this.args = args; - this.res = new ResponseTrans (); - result = null; - exception = null; + checkThread(); + wait(app.requestTimeout); - checkThread (); - wait (app.requestTimeout); - if (reqtype != NONE) { - stopThread (); - } + if (reqtype != NONE) { + stopThread(); + } - // reset res for garbage collection (res.data may hold reference to evaluator) - res = null; - if (exception != null) - throw (exception); - return result; + // reset res for garbage collection (res.data may hold reference to evaluator) + res = null; + + if (exception != null) { + throw (exception); + } + + return result; } - public Object invokeDirectFunction (Object obj, String functionName, Object[] args) throws Exception { - return scriptingEngine.invoke (obj, functionName, args, false); + /** + * + * + * @param obj ... + * @param functionName ... + * @param args ... + * + * @return ... + * + * @throws Exception ... + */ + public Object invokeDirectFunction(Object obj, String functionName, Object[] args) + throws Exception { + return scriptingEngine.invoke(obj, functionName, args, false); } - public synchronized Object invokeFunction (Object object, String functionName, Object[] args) - throws Exception { - reqtype = INTERNAL; - session = null; - thisObject = object; - method = functionName; - this.args =args; - this.res = new ResponseTrans (); - result = null; - exception = null; - - checkThread (); - wait (60000l*15); // give internal call more time (15 minutes) to complete - - if (reqtype != NONE) { - stopThread (); - } - - // reset res for garbage collection (res.data may hold reference to evaluator) - res = null; - if (exception != null) - throw (exception); - return result; + /** + * + * + * @param object ... + * @param functionName ... + * @param args ... + * + * @return ... + * + * @throws Exception ... + */ + public synchronized Object invokeFunction(Object object, String functionName, + Object[] args) + throws Exception { + // give internal call more time (15 minutes) to complete + return invokeFunction(object, functionName, args, 60000L * 15); } - public synchronized Object invokeFunction (Session session, String functionName, Object[] args) - throws Exception { - reqtype = INTERNAL; - this.session = session; - thisObject = null; - method = functionName; - this.args = args; - res = new ResponseTrans (); - result = null; - exception = null; + /** + * + * + * @param object ... + * @param functionName ... + * @param args ... + * @param timeout ... + * + * @return ... + * + * @throws Exception ... + */ + public synchronized Object invokeFunction(Object object, String functionName, + Object[] args, long timeout) + throws Exception { + reqtype = INTERNAL; + session = null; + thisObject = object; + method = functionName; + this.args = args; + this.res = new ResponseTrans(); + result = null; + exception = null; - checkThread (); - wait (app.requestTimeout); + checkThread(); + wait(timeout); - if (reqtype != NONE) { - stopThread (); - } + if (reqtype != NONE) { + stopThread(); + } - // reset res for garbage collection (res.data may hold reference to evaluator) - res = null; - if (exception != null) - throw (exception); - return result; + // reset res for garbage collection (res.data may hold reference to evaluator) + res = null; + + if (exception != null) { + throw (exception); + } + + return result; } + /** + * + * + * @param session ... + * @param functionName ... + * @param args ... + * + * @return ... + * + * @throws Exception ... + */ + public synchronized Object invokeFunction(Session session, String functionName, + Object[] args) + throws Exception { + reqtype = INTERNAL; + this.session = session; + thisObject = null; + method = functionName; + this.args = args; + res = new ResponseTrans(); + result = null; + exception = null; + + checkThread(); + wait(app.requestTimeout); + + if (reqtype != NONE) { + stopThread(); + } + + // reset res for garbage collection (res.data may hold reference to evaluator) + res = null; + + if (exception != null) { + throw (exception); + } + return result; + } /** * Stop this request evaluator's current thread. If currently active kill the request, otherwise just * notify. */ - public synchronized void stopThread () { - app.logEvent ("Stopping Thread "+rtx); - Transactor t = rtx; - // let the scripting engine know that the - // current transaction is being aborted. - if (scriptingEngine != null) - scriptingEngine.abort (); - rtx = null; - if (t != null) { - if (reqtype != NONE) { - reqtype = NONE; - t.kill (); - try { - t.abort (); - } catch (Exception ignore) {} - } else { - notifyAll (); - } - t.closeConnections (); - } + public synchronized void stopThread() { + app.logEvent("Stopping Thread " + rtx); + + Transactor t = rtx; + + // let the scripting engine know that the + // current transaction is being aborted. + if (scriptingEngine != null) { + scriptingEngine.abort(); + } + + rtx = null; + + if (t != null) { + if (reqtype != NONE) { + reqtype = NONE; + t.kill(); + + try { + t.abort(); + } catch (Exception ignore) { + } + } else { + notifyAll(); + } + + t.closeConnections(); + } } - private synchronized void checkThread () throws InterruptedException { + private synchronized void checkThread() throws InterruptedException { + if (app.stopped) { + throw new ApplicationStoppedException(); + } - if (app.stopped) - throw new ApplicationStoppedException (); - - if (rtx == null || !rtx.isAlive()) { - // app.logEvent ("Starting Thread"); - rtx = new Transactor (this, app.threadgroup, app.nmgr); - rtx.setContextClassLoader (app.getClassLoader ()); - rtx.start (); - } else { - notifyAll (); - } + if ((rtx == null) || !rtx.isAlive()) { + // app.logEvent ("Starting Thread"); + rtx = new Transactor(this, app.threadgroup, app.nmgr); + rtx.setContextClassLoader(app.getClassLoader()); + rtx.start(); + } else { + notifyAll(); + } } /** * Null out some fields, mostly for the sake of garbage collection. */ - public void recycle () { + public void recycle() { res = null; req = null; session = null; @@ -692,14 +887,17 @@ public final class RequestEvaluator implements Runnable { * Check if an action with a given name is defined for a scripted object. If it is, * return the action's function name. Otherwise, return null. */ - public String getAction (Object obj, String action) { - if (obj == null) - return null; - String act = action == null ? "main_action" : action+"_action"; - if (scriptingEngine.hasFunction (obj, act)) - return act; - return null; + public String getAction(Object obj, String action) { + if (obj == null) { + return null; + } + + String act = (action == null) ? "main_action" : (action + "_action"); + + if (scriptingEngine.hasFunction(obj, act)) { + return act; + } + + return null; } - } - diff --git a/src/helma/framework/core/Session.java b/src/helma/framework/core/Session.java index d9a8acde..f6c6e149 100644 --- a/src/helma/framework/core/Session.java +++ b/src/helma/framework/core/Session.java @@ -1,12 +1,26 @@ -// Session.java +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework.core; -import java.io.*; -import java.util.*; -import java.net.URLEncoder; import helma.objectmodel.*; import helma.objectmodel.db.*; +import java.io.*; +import java.net.URLEncoder; +import java.util.*; /** * This represents a session currently using the Hop application. @@ -14,9 +28,7 @@ import helma.objectmodel.db.*; * Depending on whether the user is logged in or not, the user object holds a * persistent user node. */ - public class Session implements Serializable { - transient Application app; String sessionID; @@ -29,118 +41,175 @@ public class Session implements Serializable { // the transient cache node that is exposed to javascript // this stays the same across logins and logouts. public TransientNode cacheNode; - - long onSince, lastTouched, lastModified; + long onSince; + long lastTouched; + long lastModified; // used to remember messages to the user between requests - // used for redirects. String message; - public Session (String sessionID, Application app) { - this.sessionID = sessionID; - this.app = app; - this.uid = null; - this.userHandle = null; - cacheNode = new TransientNode ("session"); - onSince = System.currentTimeMillis (); - lastTouched = lastModified = onSince; + /** + * Creates a new Session object. + * + * @param sessionID ... + * @param app ... + */ + public Session(String sessionID, Application app) { + this.sessionID = sessionID; + this.app = app; + this.uid = null; + this.userHandle = null; + cacheNode = new TransientNode("session"); + onSince = System.currentTimeMillis(); + lastTouched = lastModified = onSince; } /** * attach the given user node to this session. */ - public void login (INode usernode) { - if (usernode==null) { - userHandle = null; - uid = null; - } else { - userHandle = ((Node)usernode).getHandle(); - uid = usernode.getElementName(); - } - lastModified = System.currentTimeMillis (); + public void login(INode usernode) { + if (usernode == null) { + userHandle = null; + uid = null; + } else { + userHandle = ((Node) usernode).getHandle(); + uid = usernode.getElementName(); + } + + lastModified = System.currentTimeMillis(); } /** * remove this sessions's user node. */ public void logout() { - userHandle = null; - uid = null; - lastModified = System.currentTimeMillis (); + userHandle = null; + uid = null; + lastModified = System.currentTimeMillis(); } + /** + * + * + * @return ... + */ public boolean isLoggedIn() { - if (userHandle!=null && uid!=null) { - return true; - } else { - return false; - } + if ((userHandle != null) && (uid != null)) { + return true; + } else { + return false; + } } /** * Gets the user Node from this Application's NodeManager. */ public INode getUserNode() { - if (userHandle!=null) - return userHandle.getNode (app.getWrappedNodeManager()); - else - return null; + if (userHandle != null) { + return userHandle.getNode(app.getWrappedNodeManager()); + } else { + return null; + } } /** * Gets the transient cache node. */ - public INode getCacheNode () { - return cacheNode; + public INode getCacheNode() { + return cacheNode; } - public Application getApp () { - return app; + /** + * + * + * @return ... + */ + public Application getApp() { + return app; } - public void setApp (Application app) { - this.app = app; + /** + * + * + * @param app ... + */ + public void setApp(Application app) { + this.app = app; } - public String getSessionID () { - return sessionID; + /** + * + * + * @return ... + */ + public String getSessionID() { + return sessionID; } - public void touch () { - lastTouched = System.currentTimeMillis (); + /** + * + */ + public void touch() { + lastTouched = System.currentTimeMillis(); } - public long lastTouched () { - return lastTouched; + /** + * + * + * @return ... + */ + public long lastTouched() { + return lastTouched; } - public long lastModified () { - return lastModified; - } - - public void setLastModified (Date date) { - if (date != null) - lastModified = date.getTime (); + /** + * + * + * @return ... + */ + public long lastModified() { + return lastModified; } - public long onSince () { - return onSince; + /** + * + * + * @param date ... + */ + public void setLastModified(Date date) { + if (date != null) { + lastModified = date.getTime(); + } } - public String toString () { - if ( uid!=null ) - return "[Session for user " + uid + "]"; - else - return "[Anonymous Session]"; + /** + * + * + * @return ... + */ + public long onSince() { + return onSince; + } + + /** + * + * + * @return ... + */ + public String toString() { + if (uid != null) { + return "[Session for user " + uid + "]"; + } else { + return "[Anonymous Session]"; + } } /** * Get the persistent user id of a registered user. This is usually the user name, or * null if the user is not logged in. */ - public String getUID () { - return uid; + public String getUID() { + return uid; } - } - diff --git a/src/helma/framework/core/SessionBean.java b/src/helma/framework/core/SessionBean.java index 35f1c175..925756c9 100644 --- a/src/helma/framework/core/SessionBean.java +++ b/src/helma/framework/core/SessionBean.java @@ -1,79 +1,163 @@ +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.framework.core; +import helma.objectmodel.INode; +import helma.scripting.fesi.*; import java.io.Serializable; import java.util.Date; import java.util.HashMap; -import helma.objectmodel.INode; -import helma.scripting.fesi.*; - +/** + * + */ public class SessionBean implements Serializable { - // the wrapped session object Session session; - public SessionBean(Session session) { - this.session = session; + /** + * Creates a new SessionBean object. + * + * @param session ... + */ + public SessionBean(Session session) { + this.session = session; } - public String toString() { - return session.toString (); + /** + * + * + * @return ... + */ + public String toString() { + return session.toString(); } - public boolean login (String username, String password) { - boolean success = session.getApp().loginSession (username, password, session); - return success; + /** + * + * + * @param username ... + * @param password ... + * + * @return ... + */ + public boolean login(String username, String password) { + boolean success = session.getApp().loginSession(username, password, session); + + return success; } - public void logout () { - session.getApp().logoutSession (session); + /** + * + */ + public void logout() { + session.getApp().logoutSession(session); } - public void touch () { - session.touch (); + /** + * + */ + public void touch() { + session.touch(); } + /** + * + * + * @return ... + */ public Date lastActive() { - return new Date (session.lastTouched ()); + return new Date(session.lastTouched()); } + /** + * + * + * @return ... + */ public Date onSince() { - return new Date (session.onSince ()); + return new Date(session.onSince()); } // property-related methods: - public INode getdata() { - return session.getCacheNode (); + return session.getCacheNode(); } + /** + * + * + * @return ... + */ public INode getuser() { - return session.getUserNode(); + return session.getUserNode(); } - public String get_id () { - return session.getSessionID (); + /** + * + * + * @return ... + */ + public String get_id() { + return session.getSessionID(); } + /** + * + * + * @return ... + */ public String getcookie() { - return session.getSessionID (); + return session.getSessionID(); } + /** + * + * + * @return ... + */ public Date getlastActive() { - return new Date (session.lastTouched ()); + return new Date(session.lastTouched()); } + /** + * + * + * @return ... + */ public Date getonSince() { - return new Date (session.onSince ()); - } - - public Date getLastModified () { - return new Date (session.lastModified ()); + return new Date(session.onSince()); } - public void setLastModified (Date date) { - session.setLastModified (date); + /** + * + * + * @return ... + */ + public Date getLastModified() { + return new Date(session.lastModified()); } + /** + * + * + * @param date ... + */ + public void setLastModified(Date date) { + session.setLastModified(date); + } } - diff --git a/src/helma/framework/core/Skin.java b/src/helma/framework/core/Skin.java index 7febc3d5..bf561f54 100644 --- a/src/helma/framework/core/Skin.java +++ b/src/helma/framework/core/Skin.java @@ -1,15 +1,28 @@ -// Skin.java -// Copyright (c) Hannes Wallnöfer 2001 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.framework.core; import helma.framework.*; -import helma.scripting.*; -import helma.objectmodel.INode; import helma.objectmodel.ConcurrencyException; +import helma.objectmodel.INode; +import helma.scripting.*; import helma.util.HtmlEncoder; -import java.net.URLEncoder; import java.io.*; +import java.net.URLEncoder; import java.util.*; /** @@ -17,540 +30,641 @@ import java.util.*; * that will be dynamically evaluated.. It uses the request path array * from the RequestEvaluator object to resolve Macro handlers by type name. */ - public final class Skin { - - private Macro[] macros; - private Application app; - private char[] source; - private int sourceLength; - private HashSet sandbox; - - - /** - * Create a skin without any restrictions on which macros are allowed to be called from it - */ - public Skin (String content, Application app) { - this.app = app; - sandbox = null; - source = content.toCharArray (); - sourceLength = source.length; - parse (); - } - - /** - * Create a skin with a sandbox which contains the names of macros allowed to be called - */ - public Skin (String content, Application app, HashSet sandbox) { - this.app = app; - this.sandbox = sandbox; - source = content.toCharArray (); - sourceLength = source.length; - parse (); - } - - /** - * Create a skin without any restrictions on the macros from a char array. - */ - public Skin (char[] content, int length, Application app) { - this.app = app; - this.sandbox = null; - source = content; - sourceLength = length; - parse (); - } - - /** - * Parse a skin object from source text - */ - private void parse () { - - ArrayList partBuffer = new ArrayList (); - - int start = 0; - for (int i = 0; i < sourceLength-1; i++) { - if (source[i] == '<' && source[i+1] == '%') { - // found macro start tag - int j = i+2; - // search macr end tag - while (j < sourceLength-1 && (source[j] != '%' || source[j+1] != '>')) { - j++; - } - if (j > i+2) { - partBuffer.add (new Macro (i, j+2)); - start = j+2; - } - i = j+1; - } - } - - macros = new Macro[partBuffer.size()]; - partBuffer.toArray (macros); - } - - /** - * Get the raw source text this skin was parsed from - */ - public String getSource () { - return new String (source, 0, sourceLength); - } - - /** - * Render this skin - */ - public void render (RequestEvaluator reval, Object thisObject, Map paramObject) throws RedirectException { - - // check for endless skin recursion - if (++reval.skinDepth > 50) - throw new RuntimeException ("Recursive skin invocation suspected"); - - if (macros == null) { - reval.res.writeCharArray (source, 0, sourceLength); - reval.skinDepth--; - return; - } - - try { - int written = 0; - Map handlerCache = null; - if (macros.length > 3) { - handlerCache = new HashMap(); - } - for (int i=0; i written) - reval.res.writeCharArray (source, written, macros[i].start-written); - macros[i].render (reval, thisObject, paramObject, handlerCache); - written = macros[i].end; - } - if (written < sourceLength) - reval.res.writeCharArray (source, written, sourceLength-written); - } finally { - reval.skinDepth--; - } - } - - /** - * Check if a certain macro is present in this skin. The macro name is in handler.name notation - */ - public boolean containsMacro (String macroname) { - for (int i=0; i 0)) { - name = b.toString().trim(); - b.setLength (0); - state = PARAMNAME; - } else if (state == PARAMVALUE && quotechar == '\u0000') { - String paramValue = b.toString(); - if (!setSpecialParameter (lastParamName, paramValue)) - addGenericParameter (lastParamName, paramValue); - lastParamName = null; - b.setLength (0); - state = PARAMNAME; - } else if (state == PARAMVALUE) - b.append (source[i]); - else - b.setLength (0); - break; - case '=': - if (state == PARAMNAME) { - lastParamName = b.toString().trim(); - b.setLength (0); - state = PARAMVALUE; - } else - b.append (source[i]); - break; - default: - b.append (source[i]); - escape = false; - } - } - if (b.length() > 0) { - if (lastParamName != null && b.length() > 0) { - String paramValue = b.toString(); - if (!setSpecialParameter (lastParamName, paramValue)) - addGenericParameter (lastParamName, paramValue); - } else if (state <= MACRO) - name = b.toString().trim(); - } - if (handler == null) - fullName = name; - else - fullName = handler+"."+name; - - } - - - private boolean setSpecialParameter (String name, String value) { - if ("prefix".equals (name)) { - prefix = value; - return true; - } else if ("suffix".equals (name)) { - suffix = value; - return true; - } else if ("encoding".equals (name)) { - if ("html".equalsIgnoreCase (value)) - encoding = ENCODE_HTML; - else if ("xml".equalsIgnoreCase (value)) - encoding = ENCODE_XML; - else if ("form".equalsIgnoreCase (value)) - encoding = ENCODE_FORM; - else if ("url".equalsIgnoreCase (value)) - encoding = ENCODE_URL; - else if ("all".equalsIgnoreCase (value)) - encoding = ENCODE_ALL; - return true; - } else if ("default".equals (name)) { - defaultValue = value; - return true; - } - return false; - } - - private void addGenericParameter (String name, String value) { - if (parameters == null) - parameters = new HashMap (); - parameters.put (name, value); - } - - /** - * Render the macro given a handler object - */ - public void render (RequestEvaluator reval, Object thisObject, Map paramObject, Map handlerCache) throws RedirectException { - - if (sandbox != null && !sandbox.contains (fullName)) { - String h = handler == null ? "global" : handler; - reval.res.write ("[Macro "+fullName+" not allowed in sandbox]"); - return; - } else if ("response".equalsIgnoreCase (handler)) { - renderFromResponse (reval); - return; - } else if ("request".equalsIgnoreCase (handler)) { - renderFromRequest (reval); - return; - } else if ("param".equalsIgnoreCase (handler)) { - renderFromParam (reval, paramObject); - return; - } else if ("session".equalsIgnoreCase (handler)) { - renderFromSession (reval); - return; - } - - try { - - Object handlerObject = null; - - // flag to tell whether we found our invocation target object - boolean objectFound = true; - - if (handler != null) { - // try to get handler from handlerCache first - if (handlerCache != null) - handlerObject = handlerCache.get (handler); - - if (handlerObject == null) { - - // if handler object wasn't found in cache retrieve it - if (handlerObject == null && thisObject != null) { - // not a global macro - need to find handler object - // was called with this object - check it or its parents for matching prototype - if (!handler.equals ("this") && !handler.equals (app.getPrototypeName (thisObject))) { - // the handler object is not what we want - Object n = thisObject; - // walk down parent chain to find handler object - while (n != null) { - Prototype proto = app.getPrototype (n); - if (proto != null && proto.isInstanceOf (handler)) { - handlerObject = n; - break; - } - n = app.getParentElement (n); - } - } else { - // we already have the right handler object - handlerObject = thisObject; - } - } - - if (handlerObject == null) { - // eiter because thisObject == null or the right object wasn't found - // in the object's parent path. Check if a matching macro handler - // is registered with the response object (res.handlers). - handlerObject = reval.res.getMacroHandlers().get (handler); - } - - // the macro handler object couldn't be found - if (handlerObject == null) - objectFound = false; - // else put the found handler object into the cache so we don't have to look again - else if (handlerCache != null) - handlerCache.put (handler, handlerObject); - } - - } else { - // this is a global macro with no handler specified - handlerObject = null; - } - - if (objectFound) { - // check if a function called name_macro is defined. - // if so, the macro evaluates to the function. Otherwise, - // a property/field with the name is used, if defined. - - String funcName = name+"_macro"; - if (reval.scriptingEngine.hasFunction (handlerObject, funcName)) { - - StringBuffer buffer = reval.res.getBuffer(); - // remember length of response buffer before calling macro - int bufLength = buffer.length(); - // remember length of buffer with prefix written out - int preLength = 0; - if (prefix != null) { - buffer.append (prefix); - preLength = prefix.length(); - } - - // System.err.println ("Getting macro from function"); - // pass a clone of the parameter map so if the script changes it, - // Map param = ; - // if (parameters == null) - // parameters = new HashMap (); - Object[] arguments = { parameters == null ? - new HashMap () : - new HashMap (parameters) - }; - - Object value = reval.scriptingEngine.invoke (handlerObject, funcName, arguments, false); - - // check if macro wrote out to response buffer - if (buffer.length () == bufLength + preLength) { - // function didn't write out anything itself. - // erase previously written prefix - if (preLength > 0) - buffer.setLength (bufLength); - // write out macro's return value - writeResponse (value, buffer, true); - } else { - if (encoding != ENCODE_NONE) { - // if an encoding is specified, re-encode the macro's output - String output = buffer.substring (bufLength + preLength); - buffer.setLength (bufLength); - writeResponse (output, buffer, false); - } else { - // no re-encoding needed, just append suffix - if (suffix != null) - buffer.append (suffix); - } - writeResponse (value, buffer, false); - } - } else { - // System.err.println ("Getting macro from property"); - Object value = reval.scriptingEngine.get (handlerObject, name); - writeResponse (value, reval.res.getBuffer(), true); - } - } else { - String msg = "[HopMacro unhandled: "+fullName+"]"; - reval.res.write (" "+msg+" "); - app.logEvent (msg); - } - } catch (RedirectException redir) { - throw redir; - } catch (ConcurrencyException concur) { - throw concur; - } catch (TimeoutException timeout) { - throw timeout; - } catch (Exception x) { - // x.printStackTrace(); - String msg = x.getMessage(); - if (msg == null || msg.length() < 10) - msg = x.toString(); - msg = "[HopMacro error in "+fullName+": "+msg+"]"; - reval.res.write (" "+msg+" "); - app.logEvent (msg); - } - } - - private void renderFromResponse (RequestEvaluator reval) { - Object value = null; - if ("message".equals (name)) - value = reval.res.message; - else if ("error".equals (name)) - value = reval.res.error; - if (value == null) - value = reval.res.get (name); - writeResponse (value, reval.res.getBuffer(), true); - } - - private void renderFromRequest (RequestEvaluator reval) { - if (reval.req == null) - return; - Object value = reval.req.get (name); - writeResponse (value, reval.res.getBuffer(), true); - } - - private void renderFromSession (RequestEvaluator reval) { - if (reval.session == null) - return; - Object value = reval.session.getCacheNode().getString (name); - writeResponse (value, reval.res.getBuffer(), true); - } - - private void renderFromParam (RequestEvaluator reval, Map paramObject) { - if (paramObject == null) - reval.res.write ("[HopMacro error: Skin requires a parameter object]"); - else { - Object value = paramObject.get (name); - writeResponse (value, reval.res.getBuffer(), true); - } - } - - /** - * Utility method for writing text out to the response object. - */ - void writeResponse (Object value, StringBuffer buffer, boolean useDefault) { - String text; - if (value == null) { - if (useDefault) - text = defaultValue; - else - return; - } else { - text = value.toString (); - } - if (text != null && text.length() > 0) { - if (prefix != null) - buffer.append (prefix); - switch (encoding) { - case ENCODE_NONE: - buffer.append (text); - break; - case ENCODE_HTML: - HtmlEncoder.encode (text, buffer); - break; - case ENCODE_XML: - HtmlEncoder.encodeXml (text, buffer); - break; - case ENCODE_FORM: - HtmlEncoder.encodeFormValue (text, buffer); - break; - case ENCODE_URL: - buffer.append (URLEncoder.encode (text)); - break; - case ENCODE_ALL: - HtmlEncoder.encodeAll (text, buffer); - break; - } - if (suffix != null) - buffer.append (suffix); - } - } - - - public String toString () { - return "[HopMacro: "+fullName+"]"; - } - - - /** - * Return the full name of the macro in handler.name notation - */ - public String getFullName () { - return fullName; - } - + /** + * Create a skin without any restrictions on which macros are allowed to be called from it + */ + public Skin(String content, Application app) { + this.app = app; + sandbox = null; + source = content.toCharArray(); + sourceLength = source.length; + parse(); } + /** + * Create a skin with a sandbox which contains the names of macros allowed to be called + */ + public Skin(String content, Application app, HashSet sandbox) { + this.app = app; + this.sandbox = sandbox; + source = content.toCharArray(); + sourceLength = source.length; + parse(); + } + + /** + * Create a skin without any restrictions on the macros from a char array. + */ + public Skin(char[] content, int length, Application app) { + this.app = app; + this.sandbox = null; + source = content; + sourceLength = length; + parse(); + } + + /** + * Parse a skin object from source text + */ + private void parse() { + ArrayList partBuffer = new ArrayList(); + + int start = 0; + + for (int i = 0; i < (sourceLength - 1); i++) { + if ((source[i] == '<') && (source[i + 1] == '%')) { + // found macro start tag + int j = i + 2; + + // search macr end tag + while ((j < (sourceLength - 1)) && + ((source[j] != '%') || (source[j + 1] != '>'))) { + j++; + } + + if (j > (i + 2)) { + partBuffer.add(new Macro(i, j + 2)); + start = j + 2; + } + + i = j + 1; + } + } + + macros = new Macro[partBuffer.size()]; + partBuffer.toArray(macros); + } + + /** + * Get the raw source text this skin was parsed from + */ + public String getSource() { + return new String(source, 0, sourceLength); + } + + /** + * Render this skin + */ + public void render(RequestEvaluator reval, Object thisObject, Map paramObject) + throws RedirectException { + // check for endless skin recursion + if (++reval.skinDepth > 50) { + throw new RuntimeException("Recursive skin invocation suspected"); + } + + if (macros == null) { + reval.res.writeCharArray(source, 0, sourceLength); + reval.skinDepth--; + + return; + } + + try { + int written = 0; + Map handlerCache = null; + + if (macros.length > 3) { + handlerCache = new HashMap(); + } + + for (int i = 0; i < macros.length; i++) { + if (macros[i].start > written) { + reval.res.writeCharArray(source, written, macros[i].start - written); + } + + macros[i].render(reval, thisObject, paramObject, handlerCache); + written = macros[i].end; + } + + if (written < sourceLength) { + reval.res.writeCharArray(source, written, sourceLength - written); + } + } finally { + reval.skinDepth--; + } + } + + /** + * Check if a certain macro is present in this skin. The macro name is in handler.name notation + */ + public boolean containsMacro(String macroname) { + for (int i = 0; i < macros.length; i++) { + if (macros[i] instanceof Macro) { + Macro m = (Macro) macros[i]; + + if (macroname.equals(m.fullName)) { + return true; + } + } + } + + return false; + } + + /** + * Adds a macro to the list of allowed macros. The macro is in handler.name notation. + */ + public void allowMacro(String macroname) { + if (sandbox == null) { + sandbox = new HashSet(); + } + + sandbox.add(macroname); + } + + class Macro { + final int start; + final int end; + String handler; + String name; + String fullName; + String prefix; + String suffix; + String defaultValue; + int encoding = ENCODE_NONE; + Map parameters = null; + + public Macro(int start, int end) { + this.start = start; + this.end = end; + + int state = HANDLER; + boolean escape = false; + char quotechar = '\u0000'; + String lastParamName = null; + StringBuffer b = new StringBuffer(); + + for (int i = start + 2; i < (end - 2); i++) { + switch (source[i]) { + case '.': + + if (state == HANDLER) { + handler = b.toString().trim(); + b.setLength(0); + state = MACRO; + } else { + b.append(source[i]); + } + + break; + + case '\\': + + if (escape) { + b.append(source[i]); + } + + escape = !escape; + + break; + + case '"': + case '\'': + + if (!escape && (state == PARAMVALUE)) { + if (quotechar == source[i]) { + String paramValue = b.toString(); + + if (!setSpecialParameter(lastParamName, paramValue)) { + addGenericParameter(lastParamName, paramValue); + } + + lastParamName = null; + b.setLength(0); + state = PARAMNAME; + quotechar = '\u0000'; + } else if (quotechar == '\u0000') { + quotechar = source[i]; + b.setLength(0); + } else { + b.append(source[i]); + } + } else { + b.append(source[i]); + } + + escape = false; + + break; + + case ' ': + case '\t': + case '\n': + case '\r': + case '\f': + + if ((state == MACRO) || ((state == HANDLER) && (b.length() > 0))) { + name = b.toString().trim(); + b.setLength(0); + state = PARAMNAME; + } else if ((state == PARAMVALUE) && (quotechar == '\u0000')) { + String paramValue = b.toString(); + + if (!setSpecialParameter(lastParamName, paramValue)) { + addGenericParameter(lastParamName, paramValue); + } + + lastParamName = null; + b.setLength(0); + state = PARAMNAME; + } else if (state == PARAMVALUE) { + b.append(source[i]); + } else { + b.setLength(0); + } + + break; + + case '=': + + if (state == PARAMNAME) { + lastParamName = b.toString().trim(); + b.setLength(0); + state = PARAMVALUE; + } else { + b.append(source[i]); + } + + break; + + default: + b.append(source[i]); + escape = false; + } + } + + if (b.length() > 0) { + if ((lastParamName != null) && (b.length() > 0)) { + String paramValue = b.toString(); + + if (!setSpecialParameter(lastParamName, paramValue)) { + addGenericParameter(lastParamName, paramValue); + } + } else if (state <= MACRO) { + name = b.toString().trim(); + } + } + + if (handler == null) { + fullName = name; + } else { + fullName = handler + "." + name; + } + } + + private boolean setSpecialParameter(String name, String value) { + if ("prefix".equals(name)) { + prefix = value; + + return true; + } else if ("suffix".equals(name)) { + suffix = value; + + return true; + } else if ("encoding".equals(name)) { + if ("html".equalsIgnoreCase(value)) { + encoding = ENCODE_HTML; + } else if ("xml".equalsIgnoreCase(value)) { + encoding = ENCODE_XML; + } else if ("form".equalsIgnoreCase(value)) { + encoding = ENCODE_FORM; + } else if ("url".equalsIgnoreCase(value)) { + encoding = ENCODE_URL; + } else if ("all".equalsIgnoreCase(value)) { + encoding = ENCODE_ALL; + } + + return true; + } else if ("default".equals(name)) { + defaultValue = value; + + return true; + } + + return false; + } + + private void addGenericParameter(String name, String value) { + if (parameters == null) { + parameters = new HashMap(); + } + + parameters.put(name, value); + } + + /** + * Render the macro given a handler object + */ + public void render(RequestEvaluator reval, Object thisObject, Map paramObject, + Map handlerCache) throws RedirectException { + if ((sandbox != null) && !sandbox.contains(fullName)) { + String h = (handler == null) ? "global" : handler; + + reval.res.write("[Macro " + fullName + " not allowed in sandbox]"); + + return; + } else if ("response".equalsIgnoreCase(handler)) { + renderFromResponse(reval); + + return; + } else if ("request".equalsIgnoreCase(handler)) { + renderFromRequest(reval); + + return; + } else if ("param".equalsIgnoreCase(handler)) { + renderFromParam(reval, paramObject); + + return; + } else if ("session".equalsIgnoreCase(handler)) { + renderFromSession(reval); + + return; + } + + try { + Object handlerObject = null; + + // flag to tell whether we found our invocation target object + boolean objectFound = true; + + if (handler != null) { + // try to get handler from handlerCache first + if (handlerCache != null) { + handlerObject = handlerCache.get(handler); + } + + if (handlerObject == null) { + // if handler object wasn't found in cache retrieve it + if ((handlerObject == null) && (thisObject != null)) { + // not a global macro - need to find handler object + // was called with this object - check it or its parents for matching prototype + if (!handler.equals("this") && + !handler.equals(app.getPrototypeName(thisObject))) { + // the handler object is not what we want + Object n = thisObject; + + // walk down parent chain to find handler object + while (n != null) { + Prototype proto = app.getPrototype(n); + + if ((proto != null) && proto.isInstanceOf(handler)) { + handlerObject = n; + + break; + } + + n = app.getParentElement(n); + } + } else { + // we already have the right handler object + handlerObject = thisObject; + } + } + + if (handlerObject == null) { + // eiter because thisObject == null or the right object wasn't found + // in the object's parent path. Check if a matching macro handler + // is registered with the response object (res.handlers). + handlerObject = reval.res.getMacroHandlers().get(handler); + } + + // the macro handler object couldn't be found + if (handlerObject == null) { + objectFound = false; + } + // else put the found handler object into the cache so we don't have to look again + else if (handlerCache != null) { + handlerCache.put(handler, handlerObject); + } + } + } else { + // this is a global macro with no handler specified + handlerObject = null; + } + + if (objectFound) { + // check if a function called name_macro is defined. + // if so, the macro evaluates to the function. Otherwise, + // a property/field with the name is used, if defined. + String funcName = name + "_macro"; + + if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) { + StringBuffer buffer = reval.res.getBuffer(); + + // remember length of response buffer before calling macro + int bufLength = buffer.length(); + + // remember length of buffer with prefix written out + int preLength = 0; + + if (prefix != null) { + buffer.append(prefix); + preLength = prefix.length(); + } + + // System.err.println ("Getting macro from function"); + // pass a clone of the parameter map so if the script changes it, + // Map param = ; + // if (parameters == null) + // parameters = new HashMap (); + Object[] arguments = { + (parameters == null) ? new HashMap() + : new HashMap(parameters) + }; + + Object value = reval.scriptingEngine.invoke(handlerObject, + funcName, arguments, + false); + + // check if macro wrote out to response buffer + if (buffer.length() == (bufLength + preLength)) { + // function didn't write out anything itself. + // erase previously written prefix + if (preLength > 0) { + buffer.setLength(bufLength); + } + + // write out macro's return value + writeResponse(value, buffer, true); + } else { + if (encoding != ENCODE_NONE) { + // if an encoding is specified, re-encode the macro's output + String output = buffer.substring(bufLength + preLength); + + buffer.setLength(bufLength); + writeResponse(output, buffer, false); + } else { + // no re-encoding needed, just append suffix + if (suffix != null) { + buffer.append(suffix); + } + } + + writeResponse(value, buffer, false); + } + } else { + // System.err.println ("Getting macro from property"); + Object value = reval.scriptingEngine.get(handlerObject, name); + + writeResponse(value, reval.res.getBuffer(), true); + } + } else { + String msg = "[HopMacro unhandled: " + fullName + "]"; + + reval.res.write(" " + msg + " "); + app.logEvent(msg); + } + } catch (RedirectException redir) { + throw redir; + } catch (ConcurrencyException concur) { + throw concur; + } catch (TimeoutException timeout) { + throw timeout; + } catch (Exception x) { + // x.printStackTrace(); + String msg = x.getMessage(); + + if ((msg == null) || (msg.length() < 10)) { + msg = x.toString(); + } + + msg = "[HopMacro error in " + fullName + ": " + msg + "]"; + reval.res.write(" " + msg + " "); + app.logEvent(msg); + } + } + + private void renderFromResponse(RequestEvaluator reval) { + Object value = null; + + if ("message".equals(name)) { + value = reval.res.message; + } else if ("error".equals(name)) { + value = reval.res.error; + } + + if (value == null) { + value = reval.res.get(name); + } + + writeResponse(value, reval.res.getBuffer(), true); + } + + private void renderFromRequest(RequestEvaluator reval) { + if (reval.req == null) { + return; + } + + Object value = reval.req.get(name); + + writeResponse(value, reval.res.getBuffer(), true); + } + + private void renderFromSession(RequestEvaluator reval) { + if (reval.session == null) { + return; + } + + Object value = reval.session.getCacheNode().getString(name); + + writeResponse(value, reval.res.getBuffer(), true); + } + + private void renderFromParam(RequestEvaluator reval, Map paramObject) { + if (paramObject == null) { + reval.res.write("[HopMacro error: Skin requires a parameter object]"); + } else { + Object value = paramObject.get(name); + + writeResponse(value, reval.res.getBuffer(), true); + } + } + + /** + * Utility method for writing text out to the response object. + */ + void writeResponse(Object value, StringBuffer buffer, boolean useDefault) { + String text; + + if (value == null) { + if (useDefault) { + text = defaultValue; + } else { + return; + } + } else { + text = value.toString(); + } + + if ((text != null) && (text.length() > 0)) { + if (prefix != null) { + buffer.append(prefix); + } + + switch (encoding) { + case ENCODE_NONE: + buffer.append(text); + + break; + + case ENCODE_HTML: + HtmlEncoder.encode(text, buffer); + + break; + + case ENCODE_XML: + HtmlEncoder.encodeXml(text, buffer); + + break; + + case ENCODE_FORM: + HtmlEncoder.encodeFormValue(text, buffer); + + break; + + case ENCODE_URL: + buffer.append(URLEncoder.encode(text)); + + break; + + case ENCODE_ALL: + HtmlEncoder.encodeAll(text, buffer); + + break; + } + + if (suffix != null) { + buffer.append(suffix); + } + } + } + + public String toString() { + return "[HopMacro: " + fullName + "]"; + } + + /** + * Return the full name of the macro in handler.name notation + */ + public String getFullName() { + return fullName; + } + } } - - diff --git a/src/helma/framework/core/SkinFile.java b/src/helma/framework/core/SkinFile.java index bd793fc8..8b681df4 100644 --- a/src/helma/framework/core/SkinFile.java +++ b/src/helma/framework/core/SkinFile.java @@ -1,20 +1,29 @@ -// SkinFile.java -// Copyright (c) Hannes Wallnöfer 2001 - +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.framework.core; -import java.util.*; -import java.io.*; import helma.util.Updatable; - +import java.io.*; +import java.util.*; /** * This represents a File containing a Hop skin */ - - public final class SkinFile implements Updatable { - String name; Prototype prototype; Application app; @@ -22,12 +31,19 @@ public final class SkinFile implements Updatable { Skin skin; long lastmod = 0; - public SkinFile (File file, String name, Prototype proto) { - this.prototype = proto; - this.file = file; - this.name = name; - this.app = proto.app; - skin = null; + /** + * Creates a new SkinFile object. + * + * @param file ... + * @param name ... + * @param proto ... + */ + public SkinFile(File file, String name, Prototype proto) { + this.prototype = proto; + this.file = file; + this.name = name; + this.app = proto.app; + skin = null; } /** @@ -35,88 +51,113 @@ public final class SkinFile implements Updatable { * Skins contained in zipped applications. The whole update mechanism is bypassed * by immediately setting the skin member. */ - public SkinFile (String body, String name, Prototype proto) { - this.prototype = proto; - this.app = proto.app; - this.name = name; - this.file = null; - skin = new Skin (body, app); + public SkinFile(String body, String name, Prototype proto) { + this.prototype = proto; + this.app = proto.app; + this.name = name; + this.file = null; + skin = new Skin(body, app); } /** * Create a skinfile that doesn't belong to a prototype, or at * least it doesn't know about its prototype and isn't managed by the prototype. */ - public SkinFile (File file, String name, Application app) { - this.app = app; - this.file = file; - this.name = name; - this.prototype = null; - skin = null; + public SkinFile(File file, String name, Application app) { + this.app = app; + this.file = file; + this.name = name; + this.prototype = null; + skin = null; } - - /** + /** * Tell the type manager whether we need an update. this is the case when * the file has been modified or deleted. */ - public boolean needsUpdate () { - // if skin object is null we only need to call update if the file doesn't - // exist anymore, while if the skin is initialized, we'll catch both - // cases (file deleted and file changed) by just calling lastModified(). - return skin != null ? lastmod != file.lastModified () : !file.exists (); + public boolean needsUpdate() { + // if skin object is null we only need to call update if the file doesn't + // exist anymore, while if the skin is initialized, we'll catch both + // cases (file deleted and file changed) by just calling lastModified(). + return (skin != null) ? (lastmod != file.lastModified()) : (!file.exists()); } - - public void update () { - if (!file.exists ()) { - // remove skin from prototype - remove (); - } else { - // we only need to update if the skin has already been initialized - if (skin != null) - read (); - } + /** + * + */ + public void update() { + if (!file.exists()) { + // remove skin from prototype + remove(); + } else { + // we only need to update if the skin has already been initialized + if (skin != null) { + read(); + } + } } - private void read () { - try { - FileReader reader = new FileReader (file); - char c[] = new char[(int) file.length()]; - int length = reader.read (c); - reader.close(); - skin = new Skin (c, length, app); - } catch (IOException x) { - app.logEvent ("Error reading Skin "+file+": "+x); - } - lastmod = file.lastModified (); + private void read() { + try { + FileReader reader = new FileReader(file); + char[] c = new char[(int) file.length()]; + int length = reader.read(c); + + reader.close(); + skin = new Skin(c, length, app); + } catch (IOException x) { + app.logEvent("Error reading Skin " + file + ": " + x); + } + + lastmod = file.lastModified(); } - public void remove () { - if (prototype != null) { - prototype.removeSkinFile (this); - } + /** + * + */ + public void remove() { + if (prototype != null) { + prototype.removeSkinFile(this); + } } - - public File getFile () { - return file; + /** + * + * + * @return ... + */ + public File getFile() { + return file; } - public Skin getSkin () { - if (skin == null) - read (); - return skin; + /** + * + * + * @return ... + */ + public Skin getSkin() { + if (skin == null) { + read(); + } + + return skin; } - public String getName () { - return name; + /** + * + * + * @return ... + */ + public String getName() { + return name; } - public String toString () { - return prototype.getName()+"/"+file.getName(); + /** + * + * + * @return ... + */ + public String toString() { + return prototype.getName() + "/" + file.getName(); } - } - - diff --git a/src/helma/framework/core/SkinManager.java b/src/helma/framework/core/SkinManager.java index 2fef823b..b28a4fb2 100644 --- a/src/helma/framework/core/SkinManager.java +++ b/src/helma/framework/core/SkinManager.java @@ -1,107 +1,140 @@ -// SkinManager.java -// Copyright (c) Hannes Wallnöfer 2002 - +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.framework.core; -import java.util.*; import helma.objectmodel.INode; import java.io.*; - +import java.util.*; /** * Manages skins for a Helma application */ - - public final class SkinManager implements FilenameFilter { - Application app; - public SkinManager (Application app) { - this.app = app; + /** + * Creates a new SkinManager object. + * + * @param app ... + */ + public SkinManager(Application app) { + this.app = app; } + protected Skin getSkin(Prototype proto, String skinname, Object[] skinpath) { + if (proto == null) { + return null; + } - protected Skin getSkin (Prototype proto, String skinname, Object[] skinpath) { - if (proto == null) - return null; - Skin skin = null; - // First check if the skin has been already used within the execution of this request - // check for skinsets set via res.skinpath property - do { - if (skinpath != null) { - for (int i=0; i appDirMod) { - appDirMod = appDir.lastModified (); - String[] list = appDir.list (); - if (list == null) - throw new RuntimeException ("Can't read app directory "+appDir+" - check permissions"); - for (int i=0; i -1) - continue; - Prototype proto = getPrototype (list[i]); - // if prototype doesn't exist, create it - if (proto == null && isValidTypeName (list[i])) { - File f = new File (appDir, list[i]); - if (f.isDirectory ()) { - // create new prototype - createPrototype (list[i], f); - } - } - } - } + public void checkFiles() { + // check if any files have been created/removed since last time we + // checked... + if (appDir.lastModified() > appDirMod) { + appDirMod = appDir.lastModified(); - // calculate this app's checksum by adding all checksums from all prototypes - long newChecksum = 0; + String[] list = appDir.list(); - // loop through zip files to check for updates - for (Iterator it=zipfiles.values ().iterator (); it.hasNext (); ) { - ZippedAppFile zipped = (ZippedAppFile) it.next (); - if (zipped.needsUpdate ()) { - zipped.update (); - } - newChecksum += zipped.lastmod; - } + if (list == null) { + throw new RuntimeException("Can't read app directory " + appDir + + " - check permissions"); + } - // loop through prototypes and check if type.properties needs updates - // it's important that we do this _after_ potentially new prototypes - // have been created in the previous loop. - for (Iterator i=prototypes.values().iterator(); i.hasNext(); ) { - Prototype proto = (Prototype) i.next (); - // calculate this app's type checksum - newChecksum += proto.getChecksum(); - // update prototype's type mapping - DbMapping dbmap = proto.getDbMapping (); - if (dbmap != null && dbmap.needsUpdate ()) { - dbmap.update (); - if (proto != hopobjectProto && proto != globalProto) { - // set parent prototype, in case it has changed. - String parentName = dbmap.getExtends (); - if (parentName != null) - proto.setParentPrototype (getPrototype (parentName)); - else if (!app.isJavaPrototype (proto.getName())) - proto.setParentPrototype (hopobjectProto); - } - } - } - checksum = newChecksum; + for (int i = 0; i < list.length; i++) { + if (list[i].endsWith(".zip")) { + ZippedAppFile zipped = (ZippedAppFile) zipfiles.get(list[i]); + + if (zipped == null) { + File f = new File(appDir, list[i]); + + if (!f.isDirectory()) { + zipped = new ZippedAppFile(f, app); + zipfiles.put(list[i], zipped); + } + } + + continue; + } + + if (list[i].endsWith(".jar")) { + if (!jarfiles.contains(list[i])) { + jarfiles.add(list[i]); + + File f = new File(appDir, list[i]); + + try { + loader.addURL(new URL("file:" + f.getAbsolutePath())); + } catch (MalformedURLException ignore) { + } + } + + continue; + } + + if (list[i].indexOf('.') > -1) { + continue; + } + + Prototype proto = getPrototype(list[i]); + + // if prototype doesn't exist, create it + if ((proto == null) && isValidTypeName(list[i])) { + File f = new File(appDir, list[i]); + + if (f.isDirectory()) { + // create new prototype + createPrototype(list[i], f); + } + } + } + } + + // calculate this app's checksum by adding all checksums from all prototypes + long newChecksum = 0; + + // loop through zip files to check for updates + for (Iterator it = zipfiles.values().iterator(); it.hasNext();) { + ZippedAppFile zipped = (ZippedAppFile) it.next(); + + if (zipped.needsUpdate()) { + zipped.update(); + } + + newChecksum += zipped.lastmod; + } + + // loop through prototypes and check if type.properties needs updates + // it's important that we do this _after_ potentially new prototypes + // have been created in the previous loop. + for (Iterator i = prototypes.values().iterator(); i.hasNext();) { + Prototype proto = (Prototype) i.next(); + + // calculate this app's type checksum + newChecksum += proto.getChecksum(); + + // update prototype's type mapping + DbMapping dbmap = proto.getDbMapping(); + + if ((dbmap != null) && dbmap.needsUpdate()) { + dbmap.update(); + + if ((proto != hopobjectProto) && (proto != globalProto)) { + // set parent prototype, in case it has changed. + String parentName = dbmap.getExtends(); + + if (parentName != null) { + proto.setParentPrototype(getPrototype(parentName)); + } else if (!app.isJavaPrototype(proto.getName())) { + proto.setParentPrototype(hopobjectProto); + } + } + } + } + + checksum = newChecksum; } - protected void removeZipFile (String zipname) { - zipfiles.remove (zipname); - for (Iterator i=prototypes.values().iterator(); i.hasNext(); ) { - Prototype proto = (Prototype) i.next (); - // update prototype's type mapping - DbMapping dbmap = proto.getDbMapping (); - SystemProperties props = dbmap.getProperties(); - props.removeProps (zipname); - } + protected void removeZipFile(String zipname) { + zipfiles.remove(zipname); + + for (Iterator i = prototypes.values().iterator(); i.hasNext();) { + Prototype proto = (Prototype) i.next(); + + // update prototype's type mapping + DbMapping dbmap = proto.getDbMapping(); + SystemProperties props = dbmap.getProperties(); + + props.removeProps(zipname); + } } + private boolean isValidTypeName(String str) { + if (str == null) { + return false; + } - private boolean isValidTypeName (String str) { - if (str == null) - return false; - char[] c = str.toCharArray (); - for (int i=0; i 0 || (infoflags & ABORT) > 0) { - // we either timed out or there was an error. - notifyAll (); - return false; - } - if ((infoflags & WIDTH) > 0 || (infoflags & HEIGHT) > 0) { - if ((infoflags & WIDTH) > 0) - w = width; - if ((infoflags & HEIGHT) > 0) - h = height; - if (w > -1 && h > -1 && firstFrameLoaded) { - notifyAll (); - return false; - } - } - if ((infoflags & ALLBITS) > 0 || (infoflags & FRAMEBITS) > 0) { - firstFrameLoaded = true; - notifyAll (); - return false; - } - return true; - } + int getWidth() { + return w; + } + int getHeight() { + return h; + } + public synchronized boolean imageUpdate(Image img, int infoflags, int x, int y, + int width, int height) { + // check if there was an error + if (!waiting || ((infoflags & ERROR) > 0) || ((infoflags & ABORT) > 0)) { + // we either timed out or there was an error. + notifyAll(); + + return false; + } + + if (((infoflags & WIDTH) > 0) || ((infoflags & HEIGHT) > 0)) { + if ((infoflags & WIDTH) > 0) { + w = width; + } + + if ((infoflags & HEIGHT) > 0) { + h = height; + } + + if ((w > -1) && (h > -1) && firstFrameLoaded) { + notifyAll(); + + return false; + } + } + + if (((infoflags & ALLBITS) > 0) || ((infoflags & FRAMEBITS) > 0)) { + firstFrameLoaded = true; + notifyAll(); + + return false; + } + + return true; + } } - } diff --git a/src/helma/image/ImageWrapper.java b/src/helma/image/ImageWrapper.java index b5531fa8..f59fa4d9 100644 --- a/src/helma/image/ImageWrapper.java +++ b/src/helma/image/ImageWrapper.java @@ -1,247 +1,396 @@ -// ImageWrapper.java -// Copyright (c) Hannes Wallnöfer 1999-2000 +/* + * 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.image; import java.awt.*; import java.awt.image.*; -import java.util.*; +import java.io.*; import java.rmi.*; import java.rmi.server.*; -import java.io.*; +import java.util.*; -/** +/** * Abstract base class for Image Wrappers. */ - public abstract class ImageWrapper { - Image img; Graphics g; - int width, height; - int fontstyle, fontsize; + int width; + int height; + int fontstyle; + int fontsize; String fontname; ImageGenerator imggen; - public ImageWrapper (Image img, Graphics g, int width, int height, ImageGenerator imggen) { - this.img = img; - this.g = g; - this.width = width; - this.height = height; - this.imggen = imggen; - if (g != null) { - Font f = g.getFont (); - fontname = f.getName (); - fontstyle = f.getStyle (); - fontsize = f.getSize (); - } + /** + * Creates a new ImageWrapper object. + * + * @param img ... + * @param g ... + * @param width ... + * @param height ... + * @param imggen ... + */ + public ImageWrapper(Image img, Graphics g, int width, int height, + ImageGenerator imggen) { + this.img = img; + this.g = g; + this.width = width; + this.height = height; + this.imggen = imggen; + + if (g != null) { + Font f = g.getFont(); + + fontname = f.getName(); + fontstyle = f.getStyle(); + fontsize = f.getSize(); + } } /** * image manipulation methods - */ - - public void setFont (String name, int style, int size) { - this.fontname = name; - this.fontstyle = style; - this.fontsize = size; - g.setFont (new Font (name, style, size)); - } - - public void setColor (int red, int green, int blue) { - g.setColor (new Color (red, green, blue)); - } - - public void setColor (int color) { - g.setColor (new Color (color)); + */ + public void setFont(String name, int style, int size) { + this.fontname = name; + this.fontstyle = style; + this.fontsize = size; + g.setFont(new Font(name, style, size)); } - public void drawString (String str, int x, int y) { - g.drawString (str, x, y); + /** + * + * + * @param red ... + * @param green ... + * @param blue ... + */ + public void setColor(int red, int green, int blue) { + g.setColor(new Color(red, green, blue)); } - public void drawLine (int x1, int y1, int x2, int y2) { - g.drawLine (x1, y1, x2, y2); + /** + * + * + * @param color ... + */ + public void setColor(int color) { + g.setColor(new Color(color)); } - public void drawRect (int x, int y, int w, int h) { - g.drawRect (x, y, w, h); - } - - public void drawImage (String filename, int x, int y) { - try { - Image i = imggen.createImage (filename); - g.drawImage (i, x, y, null); - } catch (Exception ignore) {} + /** + * + * + * @param str ... + * @param x ... + * @param y ... + */ + public void drawString(String str, int x, int y) { + g.drawString(str, x, y); } - public void fillRect (int x, int y, int w, int h) { - g.fillRect (x, y, w, h); + /** + * + * + * @param x1 ... + * @param y1 ... + * @param x2 ... + * @param y2 ... + */ + public void drawLine(int x1, int y1, int x2, int y2) { + g.drawLine(x1, y1, x2, y2); } - public int getWidth () { - return width; + /** + * + * + * @param x ... + * @param y ... + * @param w ... + * @param h ... + */ + public void drawRect(int x, int y, int w, int h) { + g.drawRect(x, y, w, h); } - public int getHeight () { - return height; + /** + * + * + * @param filename ... + * @param x ... + * @param y ... + */ + public void drawImage(String filename, int x, int y) { + try { + Image i = imggen.createImage(filename); + + g.drawImage(i, x, y, null); + } catch (Exception ignore) { + } } - public void crop (int x, int y, int w, int h) { - ImageFilter filter = new CropImageFilter (x, y, w, h); - img = Toolkit.getDefaultToolkit ().createImage(new FilteredImageSource(img.getSource(), filter)); - + /** + * + * + * @param x ... + * @param y ... + * @param w ... + * @param h ... + */ + public void fillRect(int x, int y, int w, int h) { + g.fillRect(x, y, w, h); } - public void resize (int w, int h) { - img = img.getScaledInstance (w, h, Image.SCALE_SMOOTH); - width = w; - height = h; - } - - public void resizeFast (int w, int h) { - img = img.getScaledInstance (w, h, Image.SCALE_FAST); - width = w; - height = h; + /** + * + * + * @return ... + */ + public int getWidth() { + return width; } - public abstract void reduceColors (int colors); + /** + * + * + * @return ... + */ + public int getHeight() { + return height; + } - public abstract void saveAs (String filename); + /** + * + * + * @param x ... + * @param y ... + * @param w ... + * @param h ... + */ + public void crop(int x, int y, int w, int h) { + ImageFilter filter = new CropImageFilter(x, y, w, h); + + img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(img.getSource(), + filter)); + } + + /** + * + * + * @param w ... + * @param h ... + */ + public void resize(int w, int h) { + img = img.getScaledInstance(w, h, Image.SCALE_SMOOTH); + width = w; + height = h; + } + + /** + * + * + * @param w ... + * @param h ... + */ + public void resizeFast(int w, int h) { + img = img.getScaledInstance(w, h, Image.SCALE_FAST); + width = w; + height = h; + } + + /** + * + * + * @param colors ... + */ + public abstract void reduceColors(int colors); + + /** + * + * + * @param filename ... + */ + public abstract void saveAs(String filename); /** * Get ImageProducer of the wrapped image */ - public ImageProducer getSource () { - return img.getSource (); + public ImageProducer getSource() { + return img.getSource(); } - public void fillString (String str) { - Filler filler = new Filler (0, 0, width, height); - filler.layout (str); + /** + * + * + * @param str ... + */ + public void fillString(String str) { + Filler filler = new Filler(0, 0, width, height); + + filler.layout(str); } - public void fillString (String str, int x, int y, int w, int h) { - Filler filler = new Filler (x, y, w, h); - filler.layout (str); + /** + * + * + * @param str ... + * @param x ... + * @param y ... + * @param w ... + * @param h ... + */ + public void fillString(String str, int x, int y, int w, int h) { + Filler filler = new Filler(x, y, w, h); + + filler.layout(str); } - + class Filler { + int x; + int y; + int w; + int h; + int addedSpace = 0; + int xLeft; + int yLeft; + int realHeight; + transient Vector lines; - int x, y, w, h; - int addedSpace = 0; - int xLeft, yLeft; - int realHeight; - transient Vector lines; - - public Filler (int x, int y, int w, int h) { - this.x = x; - this.y = y; - this.w = w; - this.h = h; + public Filler(int x, int y, int w, int h) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + } + + public void layout(String str) { + int size = fontsize; + + lines = new Vector(); + + while (!splitMessage(str, size) && (size > 10)) { + lines.setSize(0); + size = Math.max(2, size - 1); + } + + Font oldfont = g.getFont(); + + g.setFont(new Font(fontname, fontstyle, size)); + + int l = lines.size(); + + for (int i = 0; i < l; i++) { + ((Line) lines.elementAt(i)).paint(g, xLeft / 2, (yLeft / 2) + y); + } + + g.setFont(oldfont); + } + + private boolean splitMessage(String string, int size) { + Font font = new Font(fontname, fontstyle, size); + FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font); + int longestLine = 0; + int heightSoFar = 0; + int heightIncrement = (int) (0.84f * metrics.getHeight()); + StringTokenizer tk = new StringTokenizer(string); + StringBuffer buffer = new StringBuffer(); + int spaceWidth = metrics.stringWidth(" "); + int currentLine = 0; + int currentWidth = 0; + int maxWidth = w - 2; + int maxHeight = (h + addedSpace) - 2; + + while (tk.hasMoreTokens()) { + String nextToken = tk.nextToken(); + int nextWidth = metrics.stringWidth(nextToken); + + if ((((currentWidth + nextWidth) >= maxWidth) && (currentWidth != 0))) { + Line line = new Line(buffer.toString(), x, heightSoFar, metrics); + + lines.addElement(line); + + if (line.textwidth > longestLine) { + longestLine = line.textwidth; + } + + buffer = new StringBuffer(); + + currentWidth = 0; + heightSoFar += heightIncrement; + } + + buffer.append(nextToken); + buffer.append(" "); + currentWidth += (nextWidth + spaceWidth); + + if (((1.18 * heightSoFar) > maxHeight) && (fontsize > 10)) { + return false; + } + } + + if (!"".equals(buffer.toString().trim())) { + Line line = new Line(buffer.toString(), x, heightSoFar, metrics); + + lines.addElement(line); + + if (line.textwidth > longestLine) { + longestLine = line.textwidth; + } + + if ((longestLine > maxWidth) && (fontsize > 10)) { + return false; + } + + heightSoFar += heightIncrement; + } + + xLeft = w - longestLine; + yLeft = (addedSpace + h) - heightSoFar; + realHeight = heightSoFar; + + return ((1.18 * heightSoFar) <= maxHeight); + } } - - public void layout (String str) { - int size = fontsize; - lines = new Vector (); - while (!splitMessage (str, size) && size > 10) { - lines.setSize (0); - size = Math.max (2, size-1); - } - Font oldfont = g.getFont (); - g.setFont (new Font (fontname, fontstyle, size)); - int l = lines.size(); - for (int i = 0; i < l; i++) { - ((Line) lines.elementAt (i)).paint (g, xLeft/2, yLeft/2 + y); - } - g.setFont (oldfont); - } - - private boolean splitMessage (String string, int size) { - - Font font = new Font (fontname, fontstyle, size); - FontMetrics metrics = Toolkit.getDefaultToolkit ().getFontMetrics (font); - int longestLine = 0; - int heightSoFar = 0; - int heightIncrement = (int) (0.84f * metrics.getHeight ()); - StringTokenizer tk = new StringTokenizer (string); - StringBuffer buffer = new StringBuffer(); - int spaceWidth = metrics.stringWidth(" "); - int currentLine = 0; - int currentWidth = 0; - int maxWidth = w - 2, maxHeight = h + addedSpace - 2; - while (tk.hasMoreTokens()) { - String nextToken = tk.nextToken(); - int nextWidth = metrics.stringWidth(nextToken); - if ((currentWidth + nextWidth >= maxWidth && currentWidth != 0)) { - Line line = new Line (buffer.toString(), x, heightSoFar, metrics); - lines.addElement (line); - if (line.textwidth > longestLine) - longestLine = line.textwidth; - buffer = new StringBuffer(); - - currentWidth = 0; - heightSoFar += heightIncrement; - } - buffer.append (nextToken); - buffer.append (" "); - currentWidth += (nextWidth + spaceWidth); - if (1.18*heightSoFar > maxHeight && fontsize > 10) - return false; - } - if (! "".equals (buffer.toString().trim())) { - Line line = new Line (buffer.toString(), x, heightSoFar, metrics); - lines.addElement (line); - - if (line.textwidth > longestLine) - longestLine = line.textwidth; - if (longestLine > maxWidth && fontsize > 10) - return false; - heightSoFar += heightIncrement; - } - xLeft = w - longestLine; - yLeft = addedSpace + h - heightSoFar; - realHeight = heightSoFar; - return (1.18*heightSoFar <= maxHeight); - } - - } - class Line implements Serializable { + String text; + int xoff; + int yoff; + FontMetrics fm; + public int textwidth; + public int len; + int ascent; - String text; - int xoff, yoff; - FontMetrics fm; - public int textwidth, len; - int ascent; + public Line(String text, int xoff, int yoff, FontMetrics fm) { + this.text = text.trim(); + len = text.length(); + this.xoff = xoff; + this.yoff = yoff; + this.fm = fm; + textwidth = (len == 0) ? 0 : fm.stringWidth(this.text); + ascent = (int) (0.9f * fm.getAscent()); + } + public void paint(Graphics g, int xadd, int yadd) { + g.drawString(text, xoff + xadd, yoff + ascent + yadd); + } - public Line (String text, int xoff, int yoff, FontMetrics fm) { - this.text = text.trim(); - len = text.length(); - this.xoff = xoff; - this.yoff = yoff; - this.fm = fm; - textwidth = (len == 0) ? 0 : fm.stringWidth(this.text); - ascent = (int) (0.9f * fm.getAscent()); + public boolean contains(int y) { + return (y < (yoff + fm.getHeight())) ? true : false; + } } - - public void paint (Graphics g, int xadd, int yadd) { - g.drawString (text, xoff+xadd, yoff+ascent+yadd); - } - - - public boolean contains (int y) { - return (y < yoff+fm.getHeight()) ? true : false; - } - - - } - } - - - - - diff --git a/src/helma/image/SunImageWrapper.java b/src/helma/image/SunImageWrapper.java index d4bc82a8..30db9dfb 100644 --- a/src/helma/image/SunImageWrapper.java +++ b/src/helma/image/SunImageWrapper.java @@ -1,87 +1,121 @@ -// ActivatedImageWrapper.java -// Copyright (c) Hannes Wallnöfer 1999-2000 +/* + * 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.image; -import java.awt.*; -import java.awt.image.*; +import Acme.JPM.Encoders.GifEncoder; import com.sun.jimi.core.*; import com.sun.jimi.core.util.*; -import Acme.JPM.Encoders.GifEncoder; -import java.io.IOException; +import java.awt.*; +import java.awt.image.*; import java.io.FileOutputStream; +import java.io.IOException; /** * A wrapper for an image that uses the Sun version of JIMI available at * http://java.sun.com/products/jimi. */ - public class SunImageWrapper extends ImageWrapper { - - public SunImageWrapper (Image img, Graphics g, int width, int height, - ImageGenerator imggen) { - super (img, g, width, height, imggen); - } - - - public void reduceColors (int colors) { - try { - int pixels[][] = getPixels(); - int palette[] = Quantize.quantizeImage(pixels, colors); - int w = pixels.length; - int h = pixels[0].length; - int pix[] = new int[w * h]; - // convert to RGB - for (int x = w; x-- > 0; ) { - for (int y = h; y-- > 0; ) { - pix[y * w + x] = palette[pixels[x][y]]; - } - } - img = imggen.createImage (new MemoryImageSource(w, h, pix, 0, w)); - } catch (Exception x) { - // throw new RuntimeException (x.getMessage ()); - } + /** + * Creates a new SunImageWrapper object. + * + * @param img ... + * @param g ... + * @param width ... + * @param height ... + * @param imggen ... + */ + public SunImageWrapper(Image img, Graphics g, int width, int height, + ImageGenerator imggen) { + super(img, g, width, height, imggen); } /** - * Snag the pixels from an image. + * + * + * @param colors ... */ - int[][] getPixels () throws IOException { - int pix[] = new int[width * height]; - PixelGrabber grabber = new PixelGrabber(img, 0, 0, width, height, pix, 0, width); - try { - if (grabber.grabPixels() != true) { - throw new IOException("Grabber returned false: " + grabber.status()); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - int pixels[][] = new int[width][height]; - for (int x = width; x-- > 0; ) { - for (int y = height; y-- > 0; ) { - pixels[x][y] = pix[y * width + x]; - } - } - return pixels; + public void reduceColors(int colors) { + try { + int[][] pixels = getPixels(); + int[] palette = Quantize.quantizeImage(pixels, colors); + int w = pixels.length; + int h = pixels[0].length; + int[] pix = new int[w * h]; + + // convert to RGB + for (int x = w; x-- > 0;) { + for (int y = h; y-- > 0;) { + pix[(y * w) + x] = palette[pixels[x][y]]; + } + } + + img = imggen.createImage(new MemoryImageSource(w, h, pix, 0, w)); + } catch (Exception x) { + // throw new RuntimeException (x.getMessage ()); + } } + /** + * Snag the pixels from an image. + */ + int[][] getPixels() throws IOException { + int[] pix = new int[width * height]; + PixelGrabber grabber = new PixelGrabber(img, 0, 0, width, height, pix, 0, width); - public void saveAs (String filename) { - try { - if (filename.toLowerCase().endsWith (".gif")) { - // sun's jimi package doesn't encode gifs, use Acme encoder - FileOutputStream fout = new FileOutputStream (filename); - // Acme gif encoder - GifEncoder enc = new GifEncoder (img, fout); - enc.encode (); - fout.close (); - } else { - Jimi.putImage (img, filename); - } - } catch (Exception x) { - throw new RuntimeException (x.getMessage ()); - } + try { + if (grabber.grabPixels() != true) { + throw new IOException("Grabber returned false: " + grabber.status()); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + int[][] pixels = new int[width][height]; + + for (int x = width; x-- > 0;) { + for (int y = height; y-- > 0;) { + pixels[x][y] = pix[(y * width) + x]; + } + } + + return pixels; } + /** + * + * + * @param filename ... + */ + public void saveAs(String filename) { + try { + if (filename.toLowerCase().endsWith(".gif")) { + // sun's jimi package doesn't encode gifs, use Acme encoder + FileOutputStream fout = new FileOutputStream(filename); + // Acme gif encoder + GifEncoder enc = new GifEncoder(img, fout); + + enc.encode(); + fout.close(); + } else { + Jimi.putImage(img, filename); + } + } catch (Exception x) { + throw new RuntimeException(x.getMessage()); + } + } } diff --git a/src/helma/main/ApplicationManager.java b/src/helma/main/ApplicationManager.java index 349a91da..af14b15e 100644 --- a/src/helma/main/ApplicationManager.java +++ b/src/helma/main/ApplicationManager.java @@ -1,33 +1,43 @@ -// ApplicationManager.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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.main; -import java.util.*; -import java.io.*; -import java.lang.reflect.*; -import java.rmi.*; -import java.rmi.server.*; -import java.net.URLEncoder; import helma.framework.*; import helma.framework.core.*; import helma.objectmodel.*; import helma.servlet.*; import helma.util.SystemProperties; +import org.apache.xmlrpc.XmlRpcHandler; import org.mortbay.http.*; import org.mortbay.http.handler.*; import org.mortbay.jetty.servlet.*; import org.mortbay.util.*; +import java.io.*; +import java.lang.reflect.*; +import java.net.URLEncoder; +import java.rmi.*; +import java.rmi.server.*; +import java.util.*; import javax.servlet.Servlet; -import org.apache.xmlrpc.XmlRpcHandler; - /** * This class is responsible for starting and stopping Helma applications. */ - public class ApplicationManager implements XmlRpcHandler { - private Hashtable applications; private Hashtable xmlrpcHandlers; private Properties mountpoints; @@ -37,258 +47,368 @@ public class ApplicationManager implements XmlRpcHandler { private Server server; private long lastModified; - public ApplicationManager (int port, File hopHome, SystemProperties props, Server server) { - this.port = port; - this.hopHome = hopHome; - this.props = props; - this.server = server; - applications = new Hashtable (); - xmlrpcHandlers = new Hashtable (); - mountpoints = new Properties (); - lastModified = 0; + /** + * Creates a new ApplicationManager object. + * + * @param port ... + * @param hopHome ... + * @param props ... + * @param server ... + */ + public ApplicationManager(int port, File hopHome, SystemProperties props, + Server server) { + this.port = port; + this.hopHome = hopHome; + this.props = props; + this.server = server; + applications = new Hashtable(); + xmlrpcHandlers = new Hashtable(); + mountpoints = new Properties(); + lastModified = 0; } - // regularely check applications property file to create and start new applications - protected void checkForChanges () { - if (props.lastModified () > lastModified) { - try { - for (Enumeration e = props.keys(); e.hasMoreElements (); ) { - String appName = (String) e.nextElement (); - if (appName.indexOf (".") == -1 && applications.get (appName) == null) { - start (appName); - register (appName); - } - } - // then stop deleted ones - for (Enumeration e = applications.keys(); e.hasMoreElements (); ) { - String appName = (String) e.nextElement (); - // check if application has been removed and should be stopped - if (!props.containsKey (appName)) { - stop (appName); - } else if (server.http != null) { - // check if application should be remounted at a - // different location on embedded web server - String oldMountpoint = mountpoints.getProperty (appName); - String mountpoint = getMountpoint (appName); - String pattern = getPathPattern (mountpoint); - 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) { - Server.getLogger().log ("Error checking applications: "+mx); - } + protected void checkForChanges() { + if (props.lastModified() > lastModified) { + try { + for (Enumeration e = props.keys(); e.hasMoreElements();) { + String appName = (String) e.nextElement(); - lastModified = System.currentTimeMillis (); - } + if ((appName.indexOf(".") == -1) && + (applications.get(appName) == null)) { + start(appName); + register(appName); + } + } + + // then stop deleted ones + for (Enumeration e = applications.keys(); e.hasMoreElements();) { + String appName = (String) e.nextElement(); + + // check if application has been removed and should be stopped + if (!props.containsKey(appName)) { + stop(appName); + } else if (server.http != null) { + // check if application should be remounted at a + // different location on embedded web server + String oldMountpoint = mountpoints.getProperty(appName); + String mountpoint = getMountpoint(appName); + String pattern = getPathPattern(mountpoint); + + 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) { + Server.getLogger().log("Error checking applications: " + mx); + } + + lastModified = System.currentTimeMillis(); + } } - void start (String appName) { - Server.getLogger().log ("Building application "+appName); - try { - // check if application and db dirs are set, otherwise go with - // the defaults, passing null dirs to the constructor. - String appDirName = props.getProperty (appName+".appdir"); - File appDir = appDirName == null ? null : new File (appDirName); - String dbDirName = props.getProperty (appName+".dbdir"); - 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 start(String appName) { + Server.getLogger().log("Building application " + appName); + + try { + // check if application and db dirs are set, otherwise go with + // the defaults, passing null dirs to the constructor. + String appDirName = props.getProperty(appName + ".appdir"); + File appDir = (appDirName == null) ? null : new File(appDirName); + String dbDirName = props.getProperty(appName + ".dbdir"); + 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); - try { - Application app = (Application) applications.get (appName); - // unbind from RMI server - 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 stop(String appName) { + Server.getLogger().log("Stopping application " + appName); + + try { + Application app = (Application) applications.get(appName); + + // unbind from RMI server + 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 (); - } + 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(); + } } - public void startAll () { - try { - for (Enumeration e = props.keys(); e.hasMoreElements (); ) { - String appName = (String) e.nextElement (); - if (appName.indexOf (".") == -1) - start (appName); - } - for (Enumeration e = props.keys(); e.hasMoreElements (); ) { - String appName = (String) e.nextElement (); - 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 (); - } catch (Exception mx) { - Server.getLogger().log ("Error starting applications: "+mx); - mx.printStackTrace (); - } + /** + * + */ + public void startAll() { + try { + for (Enumeration e = props.keys(); e.hasMoreElements();) { + String appName = (String) e.nextElement(); + + if (appName.indexOf(".") == -1) { + start(appName); + } + } + + for (Enumeration e = props.keys(); e.hasMoreElements();) { + String appName = (String) e.nextElement(); + + 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(); + } catch (Exception mx) { + Server.getLogger().log("Error starting applications: " + mx); + mx.printStackTrace(); + } } - public void stopAll () { - for (Enumeration en=applications.keys(); en.hasMoreElements(); ) { - String appName = (String) en.nextElement(); - stop (appName); - } + /** + * + */ + public void stopAll() { + for (Enumeration en = applications.keys(); en.hasMoreElements();) { + String appName = (String) en.nextElement(); + + stop(appName); + } } /** * Get an array containing all currently running applications. */ - public Object[] getApplications () { - return applications.values ().toArray (); + public Object[] getApplications() { + return applications.values().toArray(); } /** - * Get an application by name. - */ - public Application getApplication(String name) { - return (Application)applications.get(name); + * Get an application by name. + */ + public Application getApplication(String name) { + return (Application) applications.get(name); } /** * Implements org.apache.xmlrpc.XmlRpcHandler.execute() */ - public Object execute (String method, Vector params) throws Exception { - int dot = method.indexOf ("."); - if (dot == -1) - throw new Exception ("Method name \""+method+"\" does not specify a handler application"); - if (dot == 0 || dot == method.length()-1) - throw new Exception ("\""+method+"\" is not a valid XML-RPC method name"); - String handler = method.substring (0, dot); - String method2 = method.substring (dot+1); - Application app = (Application) xmlrpcHandlers.get (handler); - if (app == null) - throw new Exception ("Handler \""+handler+"\" not found for "+method); - return app.executeXmlRpc (method2, params); + public Object execute(String method, Vector params) + throws Exception { + int dot = method.indexOf("."); + + if (dot == -1) { + throw new Exception("Method name \"" + method + + "\" does not specify a handler application"); + } + + if ((dot == 0) || (dot == (method.length() - 1))) { + throw new Exception("\"" + method + "\" is not a valid XML-RPC method name"); + } + + String handler = method.substring(0, dot); + String method2 = method.substring(dot + 1); + Application app = (Application) xmlrpcHandlers.get(handler); + + if (app == null) { + throw new Exception("Handler \"" + handler + "\" not found for " + method); + } + + return app.executeXmlRpc(method2, params); } + private String getMountpoint(String appName) { + String mountpoint = props.getProperty(appName + ".mountpoint"); - private String getMountpoint (String appName) { - String mountpoint = props.getProperty (appName+".mountpoint"); - if (mountpoint == null) - return "/"+URLEncoder.encode(appName); - mountpoint = mountpoint.trim (); - if ("".equals (mountpoint)) - return "/"; - else if (!mountpoint.startsWith ("/")) - return "/"+mountpoint; - return mountpoint; + if (mountpoint == null) { + return "/" + URLEncoder.encode(appName); + } + + mountpoint = mountpoint.trim(); + + if ("".equals(mountpoint)) { + return "/"; + } else if (!mountpoint.startsWith("/")) { + return "/" + mountpoint; + } + + return mountpoint; } - private String getPathPattern (String mountpoint) { - if ("/".equals (mountpoint)) - return "/"; - if (!mountpoint.endsWith ("/")) - return mountpoint+"/*"; - return mountpoint+"*"; - } + private String getPathPattern(String mountpoint) { + if ("/".equals(mountpoint)) { + return "/"; + } + if (!mountpoint.endsWith("/")) { + return mountpoint + "/*"; + } + + return mountpoint + "*"; + } } diff --git a/src/helma/main/HelmaSecurityManager.java b/src/helma/main/HelmaSecurityManager.java index e617e4ad..31a32c6e 100644 --- a/src/helma/main/HelmaSecurityManager.java +++ b/src/helma/main/HelmaSecurityManager.java @@ -1,94 +1,327 @@ -// HelmaSecurityManager.java +/* + * 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.main; -import java.security.Permission; +import helma.framework.core.AppClassLoader; import java.io.FileDescriptor; import java.net.InetAddress; +import java.security.Permission; import java.util.HashSet; -import helma.framework.core.AppClassLoader; /** - * Liberal security manager for Helma system that makes sure application code + * Liberal security manager for Helma system that makes sure application code * is not allowed to exit the VM and set a security manager. * * This class can be subclassed to implement actual security policies. It contains - * a utility method getApplication that can be used to determine + * a utility method getApplication that can be used to determine * the name of the application trying to execute the action in question, if any. */ public class HelmaSecurityManager extends SecurityManager { - // The set of actions forbidden to application code. // We are pretty permissive, forbidding only System.exit() // and setting the security manager. - private final static HashSet forbidden = new HashSet (); + private final static HashSet forbidden = new HashSet(); + static { - forbidden.add ("exitVM"); - forbidden.add ("setSecurityManager"); + forbidden.add("exitVM"); + forbidden.add("setSecurityManager"); } + /** + * + * + * @param p ... + */ public void checkPermission(Permission p) { - if (p instanceof RuntimePermission) { - if (forbidden.contains (p.getName())) { - Class[] classes = getClassContext(); - for (int i=0; i 0) { + checkRunning(websrvPort); + } + + if (rmiPort > 0) { + checkRunning(rmiPort); + } + + if (xmlrpcPort > 0) { + checkRunning(xmlrpcPort); + } + + if (ajp13Port > 0) { + checkRunning(ajp13Port); + } + } catch (Exception running) { + System.out.println(running.getMessage()); + System.exit(1); + } + + // get hopHome from property file + if (homeDir == null) { + homeDir = sysProps.getProperty("hophome"); + } + + if (homeDir == null) { + homeDir = new File(propfile).getParent(); + } + + // create hopHome File object + hopHome = new File(homeDir); + + // try to transform hopHome directory to its cononical representation + try { + hopHome = hopHome.getCanonicalFile(); + } catch (IOException iox) { + System.err.println("Error calling getCanonicalFile() on hopHome: " + iox); + } + + // set the current working directory to the helma home dir. + // note that this is not a real cwd, which is not supported + // by java. It makes sure relative to absolute path name + // conversion is done right, so for Helma code, this should + // work. + System.setProperty("user.dir", hopHome.getPath()); + + // from now on it's safe to call getLogger() because hopHome is set up + String startMessage = "Starting Helma " + version + " on Java " + + System.getProperty("java.version"); + + getLogger().log(startMessage); + + // also print a msg to System.out + System.out.println(startMessage); + + getLogger().log("propfile = " + propfile); + getLogger().log("hopHome = " + hopHome); + + File helper = new File(hopHome, "db.properties"); + + dbPropfile = helper.getAbsolutePath(); + dbProps = new SystemProperties(dbPropfile); + DbSource.setDefaultProps(dbProps); + getLogger().log("dbPropfile = " + dbPropfile); + + appsPropfile = sysProps.getProperty("appsPropFile"); + + if ((appsPropfile != null) && !"".equals(appsPropfile.trim())) { + helper = new File(appsPropfile); + } else { + helper = new File(hopHome, "apps.properties"); + } + + appsPropfile = helper.getAbsolutePath(); + appsProps = new SystemProperties(appsPropfile); + getLogger().log("appsPropfile = " + appsPropfile); + + paranoid = "true".equalsIgnoreCase(sysProps.getProperty("paranoid")); + + String language = sysProps.getProperty("language"); + String country = sysProps.getProperty("country"); + String timezone = sysProps.getProperty("timezone"); + + if ((language != null) && (country != null)) { + Locale.setDefault(new Locale(language, country)); + } + + if (timezone != null) { + TimeZone.setDefault(TimeZone.getTimeZone(timezone)); + } + + getLogger().log("Locale = " + Locale.getDefault()); + getLogger().log("TimeZone = " + + TimeZone.getDefault().getDisplayName(Locale.getDefault())); + + dbSources = new Hashtable(); + + // try to load the extensions + extensions = new Vector(); + + if (sysProps.getProperty("extensions") != null) { + StringTokenizer tok = new StringTokenizer(sysProps.getProperty("extensions"), + ","); + + while (tok.hasMoreTokens()) { + String extClassName = tok.nextToken().trim(); + + try { + Class extClass = Class.forName(extClassName); + HelmaExtension ext = (HelmaExtension) extClass.newInstance(); + + ext.init(this); + extensions.add(ext); + getLogger().log("loaded: " + extClassName); + } catch (Exception e) { + getLogger().log("error: " + extClassName + " (" + e.toString() + + ")"); + } + } + } + } /** * static main entry point. */ - public static void main (String args[]) throws IOException { + public static void main(String[] args) throws IOException { + // check if we are running on a Java 2 VM - otherwise exit with an error message + String javaVersion = System.getProperty("java.version"); - // check if we are running on a Java 2 VM - otherwise exit with an error message - String javaVersion = System.getProperty ("java.version"); - if (javaVersion == null || javaVersion.startsWith ("1.1") || javaVersion.startsWith ("1.0")) { - System.err.println ("This version of Helma requires Java 1.2 or greater."); - if (javaVersion == null) // don't think this will ever happen, but you never know - System.err.println ("Your Java Runtime did not provide a version number. Please update to a more recent version."); - else - System.err.println ("Your Java Runtime is version "+javaVersion+". Please update to a more recent version."); - System.exit (1); - } + if ((javaVersion == null) || javaVersion.startsWith("1.1") || + javaVersion.startsWith("1.0")) { + System.err.println("This version of Helma requires Java 1.2 or greater."); - // create new server instance - server = new Server (args); + if (javaVersion == null) { // don't think this will ever happen, but you never know + System.err.println("Your Java Runtime did not provide a version number. Please update to a more recent version."); + } else { + System.err.println("Your Java Runtime is version " + javaVersion + + ". Please update to a more recent version."); + } - // start the server main thread - server.start (); + System.exit(1); + } + + // create new server instance + server = new Server(args); + + // start the server main thread + server.start(); } - /** - * Constructs a new Server instance with an array of command line options. - */ - public Server (String[] args) { - - starttime = System.currentTimeMillis(); - String homeDir = null; - - boolean usageError = false; - - // file names of various property files - String propfile = null; - String dbPropfile = "db.properties"; - String appsPropfile = null; - - // parse arguments - for (int i=0; i 0) - checkRunning (websrvPort); - if (rmiPort > 0) - checkRunning (rmiPort); - if (xmlrpcPort > 0) - checkRunning (xmlrpcPort); - if (ajp13Port > 0) - checkRunning (ajp13Port); - } catch (Exception running) { - System.out.println (running.getMessage ()); - System.exit (1); - } - - // get hopHome from property file - if (homeDir == null) - homeDir = sysProps.getProperty ("hophome"); - if (homeDir == null) - homeDir = new File (propfile).getParent (); - - // create hopHome File object - hopHome = new File (homeDir); - // try to transform hopHome directory to its cononical representation - try { - hopHome = hopHome.getCanonicalFile (); - } catch (IOException iox) { - System.err.println ("Error calling getCanonicalFile() on hopHome: "+iox); - } - - // set the current working directory to the helma home dir. - // note that this is not a real cwd, which is not supported - // by java. It makes sure relative to absolute path name - // conversion is done right, so for Helma code, this should - // work. - System.setProperty ("user.dir", hopHome.getPath()); - - // from now on it's safe to call getLogger() because hopHome is set up - - String startMessage = "Starting Helma "+version+ - " on Java "+System.getProperty ("java.version"); - getLogger().log (startMessage); - // also print a msg to System.out - System.out.println (startMessage); - - getLogger().log ("propfile = "+propfile); - getLogger().log ("hopHome = "+hopHome); - - - File helper = new File (hopHome, "db.properties"); - dbPropfile = helper.getAbsolutePath (); - dbProps = new SystemProperties (dbPropfile); - DbSource.setDefaultProps (dbProps); - getLogger().log ("dbPropfile = "+dbPropfile); - - appsPropfile = sysProps.getProperty ("appsPropFile"); - if (appsPropfile != null && !"".equals (appsPropfile.trim())) - helper = new File (appsPropfile); - else - helper = new File (hopHome, "apps.properties"); - appsPropfile = helper.getAbsolutePath (); - appsProps = new SystemProperties (appsPropfile); - getLogger().log ("appsPropfile = "+appsPropfile); - - paranoid = "true".equalsIgnoreCase (sysProps.getProperty ("paranoid")); - - String language = sysProps.getProperty ("language"); - String country = sysProps.getProperty ("country"); - String timezone = sysProps.getProperty ("timezone"); - - if (language != null && country != null) - Locale.setDefault (new Locale (language, country)); - if (timezone != null) - TimeZone.setDefault (TimeZone.getTimeZone (timezone)); - - getLogger().log ("Locale = "+Locale.getDefault()); - getLogger().log ("TimeZone = "+TimeZone.getDefault().getDisplayName (Locale.getDefault ())); - - dbSources = new Hashtable (); - - // try to load the extensions - extensions = new Vector (); - if (sysProps.getProperty ("extensions")!=null) { - StringTokenizer tok=new StringTokenizer (sysProps.getProperty ("extensions"),","); - while(tok.hasMoreTokens ()) { - String extClassName = tok.nextToken ().trim (); - try { - Class extClass = Class.forName (extClassName); - HelmaExtension ext = (HelmaExtension) extClass.newInstance (); - ext.init (this); - extensions.add (ext); - getLogger ().log ("loaded: " + extClassName); - } catch (Exception e) { - getLogger ().log ("error: " + extClassName + " (" + e.toString () + ")"); - } - } - } - + protected void start() { + // Start running, finishing setup and then entering a loop to check changes + // in the apps.properties file. + mainThread = new Thread(this); + mainThread.start(); } - protected void start () { - // Start running, finishing setup and then entering a loop to check changes - // in the apps.properties file. - mainThread = new Thread (this); - mainThread.start (); - } - - protected void stop () { - mainThread = null; + protected void stop() { + mainThread = null; } /** @@ -317,271 +364,354 @@ import org.apache.xmlrpc.*; * periodically check for changes in the apps.properties file, shutting down * apps or starting new ones. */ - public void run () { + public void run() { + try { + if ((websrvPort > 0) || (ajp13Port > 0)) { + http = new HttpServer(); - try { + // disable Jetty logging FIXME: seems to be a jetty bug; as soon + // as the logging is disabled, the more is logged + Log.instance().disableLog(); + Log.instance().add(new LogSink() { + public String getOptions() { + return null; + } - if (websrvPort > 0 || ajp13Port > 0) { - http = new HttpServer (); - // disable Jetty logging FIXME: seems to be a jetty bug; as soon - // as the logging is disabled, the more is logged - Log.instance().disableLog (); - Log.instance().add (new LogSink () - { - public String getOptions () { return null; } - public void log (String formattedLog) {} - public void log (String tag, Object msg, Frame frame, long time) {} - public void setOptions (String options) {} - public boolean isStarted () { return true; } - public void start () {} - public void stop () {} - } - ); - } + public void log(String formattedLog) { + } - // start embedded web server if port is specified - if (websrvPort > 0) { - http.addListener (new InetAddrPort (websrvPort)); - } + public void log(String tag, Object msg, Frame frame, long time) { + } - // activate the ajp13-listener - if (ajp13Port > 0) { - // create AJP13Listener - ajp13 = new AJP13Listener(new InetAddrPort (ajp13Port)); - ajp13.setHttpServer(http); - String jkallow = sysProps.getProperty ("allowAJP13"); - // by default the AJP13-connection just accepts requests from 127.0.0.1 - if (jkallow == null) - jkallow = "127.0.0.1"; - StringTokenizer st = new StringTokenizer (jkallow, " ,;"); - String[] jkallowarr = new String [st.countTokens()]; - int cnt = 0; - while (st.hasMoreTokens ()) { - jkallowarr[cnt] = st.nextToken(); - cnt++; - } - ajp13.setRemoteServers(jkallowarr); - getLogger().log ("Starting AJP13-Listener on port "+(ajp13Port)); - } + public void setOptions(String options) { + } - if (xmlrpcPort > 0) { - String xmlparser = sysProps.getProperty ("xmlparser"); - if (xmlparser != null) - XmlRpc.setDriver (xmlparser); + public boolean isStarted() { + return true; + } - xmlrpc = new WebServer (xmlrpcPort); - if (paranoid) { - xmlrpc.setParanoid (true); - String xallow = sysProps.getProperty ("allowXmlRpc"); - if (xallow != null) { - StringTokenizer st = new StringTokenizer (xallow, " ,;"); - while (st.hasMoreTokens ()) - xmlrpc.acceptClient (st.nextToken ()); - } - } - getLogger().log ("Starting XML-RPC server on port "+(xmlrpcPort)); - } + public void start() { + } + public void stop() { + } + }); + } - if (rmiPort > 0) { - if (paranoid) { - HelmaSocketFactory factory = new HelmaSocketFactory (); - String rallow = sysProps.getProperty ("allowWeb"); - if (rallow != null) { - StringTokenizer st = new StringTokenizer (rallow, " ,;"); - while (st.hasMoreTokens ()) - factory.addAddress (st.nextToken ()); - } - RMISocketFactory.setSocketFactory (factory); - } - getLogger().log ("Starting RMI server on port "+rmiPort); - LocateRegistry.createRegistry (rmiPort); - } + // start embedded web server if port is specified + if (websrvPort > 0) { + http.addListener(new InetAddrPort(websrvPort)); + } - // create application manager - appManager = new ApplicationManager (rmiPort, hopHome, appsProps, this); - if (xmlrpc != null) - xmlrpc.addHandler ("$default", appManager); + // activate the ajp13-listener + if (ajp13Port > 0) { + // create AJP13Listener + ajp13 = new AJP13Listener(new InetAddrPort(ajp13Port)); + ajp13.setHttpServer(http); - // add shutdown hook to close running apps and servers on exit - Runtime.getRuntime().addShutdownHook (new HelmaShutdownHook(appManager)); + String jkallow = sysProps.getProperty("allowAJP13"); - } catch (Exception gx) { - getLogger().log ("Error initializing embedded database: "+gx); - gx.printStackTrace (); - return; - } + // by default the AJP13-connection just accepts requests from 127.0.0.1 + if (jkallow == null) { + jkallow = "127.0.0.1"; + } - // set the security manager. - // the default implementation is helma.main.HelmaSecurityManager. - try { - String secManClass = sysProps.getProperty ("securityManager"); - if (secManClass != null) { - SecurityManager secMan = (SecurityManager) Class.forName(secManClass).newInstance (); - System.setSecurityManager (secMan); - getLogger().log ("Setting security manager to "+secManClass); - } - } catch (Exception x) { - getLogger().log ("Error setting security manager: "+x); - } + StringTokenizer st = new StringTokenizer(jkallow, " ,;"); + String[] jkallowarr = new String[st.countTokens()]; + int cnt = 0; - // start applications - appManager.startAll (); + while (st.hasMoreTokens()) { + jkallowarr[cnt] = st.nextToken(); + cnt++; + } - // start embedded web server - if (http != null) try { - http.start (); - } catch (MultiException m) { - getLogger().log ("Error starting embedded web server: "+m); - } + ajp13.setRemoteServers(jkallowarr); + getLogger().log("Starting AJP13-Listener on port " + (ajp13Port)); + } - if (ajp13 != null) try { - ajp13.start (); - } catch (Exception m) { - getLogger().log ("Error starting AJP13 listener: "+m); - } + if (xmlrpcPort > 0) { + String xmlparser = sysProps.getProperty("xmlparser"); - int count = 0; - while (Thread.currentThread () == mainThread) { - try { - mainThread.sleep (3000l); - } catch (InterruptedException ie) {} - try { - appManager.checkForChanges (); - } catch (Exception x) { - getLogger().log ("Caught in app manager loop: "+x); - } - } + if (xmlparser != null) { + XmlRpc.setDriver(xmlparser); + } + + xmlrpc = new WebServer(xmlrpcPort); + + if (paranoid) { + xmlrpc.setParanoid(true); + + String xallow = sysProps.getProperty("allowXmlRpc"); + + if (xallow != null) { + StringTokenizer st = new StringTokenizer(xallow, " ,;"); + + while (st.hasMoreTokens()) + xmlrpc.acceptClient(st.nextToken()); + } + } + + getLogger().log("Starting XML-RPC server on port " + (xmlrpcPort)); + } + + if (rmiPort > 0) { + if (paranoid) { + HelmaSocketFactory factory = new HelmaSocketFactory(); + String rallow = sysProps.getProperty("allowWeb"); + + if (rallow != null) { + StringTokenizer st = new StringTokenizer(rallow, " ,;"); + + while (st.hasMoreTokens()) + factory.addAddress(st.nextToken()); + } + + RMISocketFactory.setSocketFactory(factory); + } + + getLogger().log("Starting RMI server on port " + rmiPort); + LocateRegistry.createRegistry(rmiPort); + } + + // create application manager + appManager = new ApplicationManager(rmiPort, hopHome, appsProps, this); + + if (xmlrpc != null) { + xmlrpc.addHandler("$default", appManager); + } + + // add shutdown hook to close running apps and servers on exit + Runtime.getRuntime().addShutdownHook(new HelmaShutdownHook(appManager)); + } catch (Exception gx) { + getLogger().log("Error initializing embedded database: " + gx); + gx.printStackTrace(); + + return; + } + + // set the security manager. + // the default implementation is helma.main.HelmaSecurityManager. + try { + String secManClass = sysProps.getProperty("securityManager"); + + if (secManClass != null) { + SecurityManager secMan = (SecurityManager) Class.forName(secManClass) + .newInstance(); + + System.setSecurityManager(secMan); + getLogger().log("Setting security manager to " + secManClass); + } + } catch (Exception x) { + getLogger().log("Error setting security manager: " + x); + } + + // start applications + appManager.startAll(); + + // start embedded web server + if (http != null) { + try { + http.start(); + } catch (MultiException m) { + getLogger().log("Error starting embedded web server: " + m); + } + } + + if (ajp13 != null) { + try { + ajp13.start(); + } catch (Exception m) { + getLogger().log("Error starting AJP13 listener: " + m); + } + } + + int count = 0; + + while (Thread.currentThread() == mainThread) { + try { + mainThread.sleep(3000L); + } catch (InterruptedException ie) { + } + + try { + appManager.checkForChanges(); + } catch (Exception x) { + getLogger().log("Caught in app manager loop: " + x); + } + } } /** * Get an Iterator over the applications currently running on this Server. */ - public Object[] getApplications () { - return appManager.getApplications (); + public Object[] getApplications() { + return appManager.getApplications(); } /** * Get an Application by name */ - public Application getApplication(String name) { - return appManager.getApplication(name); + public Application getApplication(String name) { + return appManager.getApplication(name); } /** * Get a logger to use for output in this server. */ - public static Logger getLogger () { - if (logger == null) { - String logDir = sysProps.getProperty ("logdir", "log"); - if ("console".equalsIgnoreCase (logDir)) { - logger = new Logger (System.out); - } else { - File helper = new File (logDir); - if (hopHome != null && !helper.isAbsolute ()) - helper = new File (hopHome, logDir); - logDir = helper.getAbsolutePath (); - logger = Logger.getLogger (logDir, "hop"); - } - } - return logger; + public static Logger getLogger() { + if (logger == null) { + String logDir = sysProps.getProperty("logdir", "log"); + + if ("console".equalsIgnoreCase(logDir)) { + logger = new Logger(System.out); + } else { + File helper = new File(logDir); + + if ((hopHome != null) && !helper.isAbsolute()) { + helper = new File(hopHome, logDir); + } + + logDir = helper.getAbsolutePath(); + logger = Logger.getLogger(logDir, "hop"); + } + } + + return logger; } /** * Get the Home directory of this server. */ - public File getHopHome () { - return hopHome; + public File getHopHome() { + return hopHome; } /** * Get the main Server instance. */ - public static Server getServer() { - return server; + public static Server getServer() { + return server; } /** - * Get the Server's XML-RPC web server. - */ + * Get the Server's XML-RPC web server. + */ public static WebServer getXmlRpcServer() { - return server.xmlrpc; + return server.xmlrpc; } /** * A primitive method to check whether a server is already running on our port. */ - private void checkRunning (int portNumber) throws Exception { - try { - java.net.Socket socket = new java.net.Socket ("localhost", portNumber); - } catch (Exception x) { - return; - } - // if we got so far, another server is already running on this port and db - throw new Exception ("Error: Server already running on this port: " + portNumber); + private void checkRunning(int portNumber) throws Exception { + try { + java.net.Socket socket = new java.net.Socket("localhost", portNumber); + } catch (Exception x) { + return; + } + + // if we got so far, another server is already running on this port and db + throw new Exception("Error: Server already running on this port: " + portNumber); } - public String getProperty( String key ) { - return (String)sysProps.get(key); - } + /** + * + * + * @param key ... + * + * @return ... + */ + public String getProperty(String key) { + return (String) sysProps.get(key); + } - public SystemProperties getProperties() { - return sysProps; - } - - public SystemProperties getDbProperties() { - return dbProps; - } + /** + * + * + * @return ... + */ + public SystemProperties getProperties() { + return sysProps; + } - public File getAppsHome() { - String appHome = sysProps.getProperty ("appHome"); - if (appHome != null && !"".equals (appHome.trim())) - return new File (appHome); - else - return new File (hopHome, "apps"); - } + /** + * + * + * @return ... + */ + public SystemProperties getDbProperties() { + return dbProps; + } - public Vector getExtensions () { - return extensions; - } + /** + * + * + * @return ... + */ + public File getAppsHome() { + String appHome = sysProps.getProperty("appHome"); - public void startApplication(String name) { - appManager.start (name); - appManager.register (name); - } + if ((appHome != null) && !"".equals(appHome.trim())) { + return new File(appHome); + } else { + return new File(hopHome, "apps"); + } + } - public void stopApplication(String name) { - appManager.stop (name); - } + /** + * + * + * @return ... + */ + public Vector getExtensions() { + return extensions; + } - /** - * method from helma.framework.IPathElement - */ - public String getElementName() { - return "root"; - } + /** + * + * + * @param name ... + */ + public void startApplication(String name) { + appManager.start(name); + appManager.register(name); + } - /** - * method from helma.framework.IPathElement, - * returning active applications - */ - public IPathElement getChildElement(String name) { - return appManager.getApplication(name); - } + /** + * + * + * @param name ... + */ + public void stopApplication(String name) { + appManager.stop(name); + } - /** - * method from helma.framework.IPathElement - */ - public IPathElement getParentElement() { - return null; - } + /** + * method from helma.framework.IPathElement + */ + public String getElementName() { + return "root"; + } - /** - * method from helma.framework.IPathElement - */ - public String getPrototype() { - return "root"; - } - + /** + * method from helma.framework.IPathElement, + * returning active applications + */ + public IPathElement getChildElement(String name) { + return appManager.getApplication(name); + } + + /** + * method from helma.framework.IPathElement + */ + public IPathElement getParentElement() { + return null; + } + + /** + * method from helma.framework.IPathElement + */ + public String getPrototype() { + return "root"; + } } - diff --git a/src/helma/main/launcher/FilteredClassLoader.java b/src/helma/main/launcher/FilteredClassLoader.java index 07802996..4e2ceef4 100644 --- a/src/helma/main/launcher/FilteredClassLoader.java +++ b/src/helma/main/launcher/FilteredClassLoader.java @@ -1,36 +1,51 @@ -// FilteredClassLoader.java +/* + * 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.main.launcher; import java.net.URL; import java.net.URLClassLoader; -import java.util.Hashtable; +import java.security.CodeSource; import java.security.PermissionCollection; import java.security.Permissions; import java.security.Policy; -import java.security.CodeSource; +import java.util.Hashtable; /** * ClassLoader that is able to exclude certain packages from loading. */ public class FilteredClassLoader extends URLClassLoader { - - /** - * Create a server wide class loader that doesn't see the scripting engine(s) - * embedded in helma.jar. These files should be loaded by the per-application - * class loaders so that special security policies can be applied to them and - * so that they can load classes from jar files in the app directories. - */ + /** + * Create a server wide class loader that doesn't see the scripting engine(s) + * embedded in helma.jar. These files should be loaded by the per-application + * class loaders so that special security policies can be applied to them and + * so that they can load classes from jar files in the app directories. + */ public FilteredClassLoader(URL[] urls) { - super (urls); + super(urls); } /** * Mask classes that implement the scripting engine(s) contained in helma.jar */ - protected Class findClass (String name) throws ClassNotFoundException { - if (name != null && "helma.scripting.fesi.PhantomEngine".equals (name)) - throw new ClassNotFoundException (name); - return super.findClass (name); + protected Class findClass(String name) throws ClassNotFoundException { + if ((name != null) && "helma.scripting.fesi.PhantomEngine".equals(name)) { + throw new ClassNotFoundException(name); + } + + return super.findClass(name); } } diff --git a/src/helma/main/launcher/Main.java b/src/helma/main/launcher/Main.java index e09f94f9..256f129b 100644 --- a/src/helma/main/launcher/Main.java +++ b/src/helma/main/launcher/Main.java @@ -1,114 +1,145 @@ -// helma.main.Main +/* + * 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.main.launcher; -import java.net.URLClassLoader; -import java.net.URL; -import java.net.URLDecoder; import java.io.File; import java.io.FilenameFilter; import java.lang.reflect.Method; -import java.util.ArrayList; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLDecoder; import java.security.Policy; +import java.util.ArrayList; -/** - * Helma bootstrap class. Figures out Helma home directory, sets up class path and +/** + * Helma bootstrap class. Figures out Helma home directory, sets up class path and * lauchnes main class. */ public class Main { - public static final String[] jars = { - "helma.jar", - "jetty.jar", - "crimson.jar", - "xmlrpc.jar", - "servlet.jar", - "regexp.jar", - "mail.jar", - "activation.jar", - "netcomponents.jar", - "jimi.jar", - "apache-dom.jar", - "jdom.jar" - }; + "helma.jar", "jetty.jar", "crimson.jar", + "xmlrpc.jar", "servlet.jar", "regexp.jar", + "mail.jar", "activation.jar", + "netcomponents.jar", "jimi.jar", + "apache-dom.jar", "jdom.jar" + }; + /** + * + * + * @param args ... + * + * @throws Exception ... + */ + public static void main(String[] args) throws Exception { + // check if home directory is set via command line arg. If not, + // we'll get it from the location of the jar file this class + // has been loaded from. + String installDir = null; - public static void main (String[] args) throws Exception { + // first, try to get helma home dir from command line options + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-i") && ((i + 1) < args.length)) { + installDir = args[i + 1]; + } + } - // check if home directory is set via command line arg. If not, - // we'll get it from the location of the jar file this class - // has been loaded from. - String installDir = null; - // first, try to get helma home dir from command line options - for (int i=0; i!/{entry} - // we strip away the jar: prefix and the !/{entry} suffix - // to get the original jar file URL - installDir = launcherUrl.toString().substring(4); - int excl = installDir.indexOf ("!"); - if (excl > -1) { - installDir = installDir.substring(0, excl); - launcherUrl = new URL (installDir); - File f = new File (launcherUrl.getPath()); - installDir = f.getParentFile().getCanonicalPath(); - } - } catch (Exception x) { - // unable to get Helma installation dir from launcher jar - System.err.println ("Unable to get Helma installation directory: "+x); - System.exit (2); - } - } + URLClassLoader apploader = (URLClassLoader) ClassLoader.getSystemClassLoader(); - // decode installDir in case it is URL-encoded - installDir = URLDecoder.decode (installDir); + // try to get Helma installation directory + if (installDir == null) { + try { + URL launcherUrl = apploader.findResource("helma/main/launcher/Main.class"); - // set up the class path - File libdir = new File (installDir, "lib"); - ArrayList jarlist = new ArrayList (); - for (int i=0;i!/{entry} + // we strip away the jar: prefix and the !/{entry} suffix + // to get the original jar file URL + installDir = launcherUrl.toString().substring(4); + + int excl = installDir.indexOf("!"); + + if (excl > -1) { + installDir = installDir.substring(0, excl); + launcherUrl = new URL(installDir); + + File f = new File(launcherUrl.getPath()); + + installDir = f.getParentFile().getCanonicalPath(); + } + } catch (Exception x) { + // unable to get Helma installation dir from launcher jar + System.err.println("Unable to get Helma installation directory: " + x); + System.exit(2); + } + } + + // decode installDir in case it is URL-encoded + installDir = URLDecoder.decode(installDir); + + // set up the class path + File libdir = new File(installDir, "lib"); + ArrayList jarlist = new ArrayList(); + + for (int i = 0; i < jars.length; i++) { + File jar = new File(libdir, jars[i]); + + jarlist.add(new URL("file:" + jar.getAbsolutePath())); + } + + // add all jar files from the lib/ext directory + File extdir = new File(libdir, "ext"); + File[] files = extdir.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + String n = name.toLowerCase(); + + return n.endsWith(".jar") || n.endsWith(".zip"); + } + }); + + if (files != null) { + for (int i = 0; i < files.length; i++) { + // WORKAROUND: add the files in lib/ext before + // lib/apache-dom.jar, since otherwise putting a full version + // of Xerces in lib/ext would cause a version conflict with the + // xerces classes in lib/apache-dom.jar. Generally, having some pieces + // of Xerces in lib/apache-dom.jar is kind of problematic. + jarlist.add(jars.length - 3, new URL("file:" + + files[i].getAbsolutePath())); + System.err.println("Adding to classpath: " + files[i].getAbsolutePath()); + } + } + + URL[] urls = new URL[jarlist.size()]; + + jarlist.toArray(urls); + + FilteredClassLoader loader = new FilteredClassLoader(urls); + + // set the new class loader as context class loader + Thread.currentThread().setContextClassLoader(loader); + + // get the main server class + Class clazz = loader.loadClass("helma.main.Server"); + Class[] cargs = new Class[] { args.getClass() }; + Method main = clazz.getMethod("main", cargs); + Object[] nargs = new Object[] { args }; + + // run + main.invoke(null, nargs); } - } diff --git a/src/helma/objectmodel/ConcurrencyException.java b/src/helma/objectmodel/ConcurrencyException.java index f6684b5d..d495e9c2 100644 --- a/src/helma/objectmodel/ConcurrencyException.java +++ b/src/helma/objectmodel/ConcurrencyException.java @@ -1,51 +1,33 @@ -// ConcurrencyException.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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; /** - * Thrown when more than one thrad tries to modify a Node. The evaluator - * will normally catch this and try again after a period of time. + * Thrown when more than one thrad tries to modify a Node. The evaluator + * will normally catch this and try again after a period of time. */ - public class ConcurrencyException extends RuntimeException { - - public ConcurrencyException (String msg) { - super (msg); + /** + * Creates a new ConcurrencyException object. + * + * @param msg ... + */ + public ConcurrencyException(String msg) { + super(msg); } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/objectmodel/DatabaseException.java b/src/helma/objectmodel/DatabaseException.java index dbd794a3..1dfe2396 100644 --- a/src/helma/objectmodel/DatabaseException.java +++ b/src/helma/objectmodel/DatabaseException.java @@ -1,49 +1,32 @@ -// DatabaseException.java - +/* + * 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; /** * Thrown on any kind of Database-Error */ - public class DatabaseException extends RuntimeException { - - public DatabaseException (String msg) { - super (msg); + /** + * Creates a new DatabaseException object. + * + * @param msg ... + */ + public DatabaseException(String msg) { + super(msg); } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/objectmodel/IDatabase.java b/src/helma/objectmodel/IDatabase.java index 2618d621..a9a7e2da 100644 --- a/src/helma/objectmodel/IDatabase.java +++ b/src/helma/objectmodel/IDatabase.java @@ -1,35 +1,108 @@ -// IDatabase.java +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.objectmodel; -import helma.objectmodel.db.IDGenerator; import helma.objectmodel.INode; +import helma.objectmodel.db.IDGenerator; +import java.io.IOException; +import org.xml.sax.SAXException; +import javax.xml.parsers.ParserConfigurationException; /** * Interface that is implemented by Database wrappers */ - public interface IDatabase { + // db-related + public void shutdown(); - // db-related - public void shutdown (); + // id-related + public String nextID() throws ObjectNotFoundException; - // id-related - public String nextID() throws ObjectNotFoundException; - public IDGenerator getIDGenerator (ITransaction transaction) throws Exception; - public void saveIDGenerator (ITransaction transaction, IDGenerator idgen) throws Exception; + /** + * + * + * @param transaction ... + * + * @return ... + * + * @throws IOException ... + */ + public IDGenerator getIDGenerator(ITransaction transaction) + throws IOException, ObjectNotFoundException; - // node-related - public INode getNode (ITransaction transaction, String key) throws Exception; - public void saveNode (ITransaction transaction, String key, INode node) throws Exception; - public void deleteNode (ITransaction transaction, String key) throws Exception; + /** + * + * + * @param transaction ... + * @param idgen ... + * + * @throws IOException ... + */ + public void saveIDGenerator(ITransaction transaction, IDGenerator idgen) + throws IOException; - // transaction-related - public ITransaction beginTransaction (); - public void commitTransaction (ITransaction transaction) throws DatabaseException; - public void abortTransaction (ITransaction transaction) throws DatabaseException; + // node-related + public INode getNode(ITransaction transaction, String key) + throws IOException, ObjectNotFoundException, + SAXException, ParserConfigurationException; + /** + * + * + * @param transaction ... + * @param key ... + * @param node ... + * + * @throws IOException ... + */ + public void saveNode(ITransaction transaction, String key, INode node) + throws IOException; + + /** + * + * + * @param transaction ... + * @param key ... + * + * @throws IOException ... + */ + public void deleteNode(ITransaction transaction, String key) + throws IOException; + + // transaction-related + public ITransaction beginTransaction(); + + /** + * + * + * @param transaction ... + * + * @throws DatabaseException ... + */ + public void commitTransaction(ITransaction transaction) + throws DatabaseException; + + /** + * + * + * @param transaction ... + * + * @throws DatabaseException ... + */ + public void abortTransaction(ITransaction transaction) + throws DatabaseException; } - - - diff --git a/src/helma/objectmodel/INode.java b/src/helma/objectmodel/INode.java index 4bab60c2..d03cb6bd 100644 --- a/src/helma/objectmodel/INode.java +++ b/src/helma/objectmodel/INode.java @@ -1,88 +1,391 @@ -// INode.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.objectmodel; -import java.util.*; -import java.io.*; import helma.framework.IPathElement; import helma.objectmodel.db.DbMapping; +import java.io.*; +import java.util.*; /** * Interface that all Nodes implement. Currently, there are two implementations: * Transient nodes which only exist in memory, and persistent Nodes, which are * stored in a database (either the internal Object DB or an external relational DB). */ - public interface INode extends INodeState, IPathElement { - - - /** + /** * id-related methods */ + public String getID(); - public String getID (); - public String getName (); - public void setDbMapping (DbMapping dbmap); - public DbMapping getDbMapping (); - public int getState (); - public void setState (int s); - public void setName (String name); - public long lastModified (); - public long created (); - public boolean isAnonymous (); // is this a named node, or an anonymous node in a collection? - public String getPrototype (); - public void setPrototype (String prototype); - public INode getCacheNode (); - public void clearCacheNode (); - public String getFullName (); - public String getFullName (INode root); + /** + * + * + * @return ... + */ + public String getName(); + + /** + * + * + * @param dbmap ... + */ + public void setDbMapping(DbMapping dbmap); + + /** + * + * + * @return ... + */ + public DbMapping getDbMapping(); + + /** + * + * + * @return ... + */ + public int getState(); + + /** + * + * + * @param s ... + */ + public void setState(int s); + + /** + * + * + * @param name ... + */ + public void setName(String name); + + /** + * + * + * @return ... + */ + public long lastModified(); + + /** + * + * + * @return ... + */ + public long created(); + + /** + * + * + * @return ... + */ + public boolean isAnonymous(); // is this a named node, or an anonymous node in a collection? + + /** + * + * + * @return ... + */ + public String getPrototype(); + + /** + * + * + * @param prototype ... + */ + public void setPrototype(String prototype); + + /** + * + * + * @return ... + */ + public INode getCacheNode(); + + /** + * + */ + public void clearCacheNode(); + + /** + * + * + * @return ... + */ + public String getFullName(); + + /** + * + * + * @param root ... + * + * @return ... + */ + public String getFullName(INode root); /** * node-related methods */ + public INode getParent(); - public INode getParent (); - public void setSubnodeRelation (String rel); - public String getSubnodeRelation (); - public int numberOfNodes (); - public INode addNode (INode node); - public INode addNode (INode node, int where); - public INode createNode (String name); - public INode createNode (String name, int where); - public Enumeration getSubnodes (); - public INode getSubnode (String name); - public INode getSubnodeAt (int index); - public int contains (INode node); - public boolean remove (); - public void removeNode (INode node); + /** + * + * + * @param rel ... + */ + public void setSubnodeRelation(String rel); + + /** + * + * + * @return ... + */ + public String getSubnodeRelation(); + + /** + * + * + * @return ... + */ + public int numberOfNodes(); + + /** + * + * + * @param node ... + * + * @return ... + */ + public INode addNode(INode node); + + /** + * + * + * @param node ... + * @param where ... + * + * @return ... + */ + public INode addNode(INode node, int where); + + /** + * + * + * @param name ... + * + * @return ... + */ + public INode createNode(String name); + + /** + * + * + * @param name ... + * @param where ... + * + * @return ... + */ + public INode createNode(String name, int where); + + /** + * + * + * @return ... + */ + public Enumeration getSubnodes(); + + /** + * + * + * @param name ... + * + * @return ... + */ + public INode getSubnode(String name); + + /** + * + * + * @param index ... + * + * @return ... + */ + public INode getSubnodeAt(int index); + + /** + * + * + * @param node ... + * + * @return ... + */ + public int contains(INode node); + + /** + * + * + * @return ... + */ + public boolean remove(); + + /** + * + * + * @param node ... + */ + public void removeNode(INode node); /** * property-related methods */ + public Enumeration properties(); - public Enumeration properties (); - public IProperty get (String name); - public String getString (String name); - public boolean getBoolean (String name); - public Date getDate (String name); - public long getInteger (String name); - public double getFloat (String name); - public INode getNode (String name); - public Object getJavaObject (String name); + /** + * + * + * @param name ... + * + * @return ... + */ + public IProperty get(String name); - public void setString (String name, String value); - public void setBoolean (String name, boolean value); - public void setDate (String name, Date value); - public void setInteger (String name, long value); - public void setFloat (String name, double value); - public void setNode (String name, INode value); - public void setJavaObject (String name, Object value); + /** + * + * + * @param name ... + * + * @return ... + */ + public String getString(String name); - public void unset (String name); + /** + * + * + * @param name ... + * + * @return ... + */ + public boolean getBoolean(String name); + /** + * + * + * @param name ... + * + * @return ... + */ + public Date getDate(String name); + + /** + * + * + * @param name ... + * + * @return ... + */ + public long getInteger(String name); + + /** + * + * + * @param name ... + * + * @return ... + */ + public double getFloat(String name); + + /** + * + * + * @param name ... + * + * @return ... + */ + public INode getNode(String name); + + /** + * + * + * @param name ... + * + * @return ... + */ + public Object getJavaObject(String name); + + /** + * + * + * @param name ... + * @param value ... + */ + public void setString(String name, String value); + + /** + * + * + * @param name ... + * @param value ... + */ + public void setBoolean(String name, boolean value); + + /** + * + * + * @param name ... + * @param value ... + */ + public void setDate(String name, Date value); + + /** + * + * + * @param name ... + * @param value ... + */ + public void setInteger(String name, long value); + + /** + * + * + * @param name ... + * @param value ... + */ + public void setFloat(String name, double value); + + /** + * + * + * @param name ... + * @param value ... + */ + public void setNode(String name, INode value); + + /** + * + * + * @param name ... + * @param value ... + */ + public void setJavaObject(String name, Object value); + + /** + * + * + * @param name ... + */ + public void unset(String name); } - - - - diff --git a/src/helma/objectmodel/INodeState.java b/src/helma/objectmodel/INodeState.java index 71d29773..df91a6a6 100644 --- a/src/helma/objectmodel/INodeState.java +++ b/src/helma/objectmodel/INodeState.java @@ -1,17 +1,28 @@ -// INodeState.java -// Copyright (c) Hannes Wallnöfer 2001 - +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.objectmodel; -import java.util.*; import java.io.*; +import java.util.*; /** * Interface that defines states of nodes */ - public interface INodeState { - public final static int TRANSIENT = -3; public final static int VIRTUAL = -2; public final static int INVALID = -1; @@ -19,9 +30,4 @@ public interface INodeState { public final static int NEW = 1; public final static int MODIFIED = 2; public final static int DELETED = 3; - } - - - - diff --git a/src/helma/objectmodel/IProperty.java b/src/helma/objectmodel/IProperty.java index 3016fac5..95198079 100644 --- a/src/helma/objectmodel/IProperty.java +++ b/src/helma/objectmodel/IProperty.java @@ -1,5 +1,18 @@ -// IProperty.java -// Copyright (c) Hannes Wallnöfer 1997-2000 +/* + * 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; @@ -8,9 +21,7 @@ import java.util.Date; /** * Interface that is implemented by node properties. */ - public interface IProperty { - public static final int STRING = 1; public static final int BOOLEAN = 2; public static final int DATE = 3; @@ -19,16 +30,73 @@ public interface IProperty { public static final int NODE = 6; public static final int JAVAOBJECT = 7; - public String getName (); - public int getType (); - public Object getValue (); + /** + * + * + * @return ... + */ + public String getName(); - public INode getNodeValue (); - public String getStringValue (); - public boolean getBooleanValue (); - public long getIntegerValue (); - public double getFloatValue (); - public Date getDateValue (); - public Object getJavaObjectValue (); + /** + * + * + * @return ... + */ + public int getType(); - } \ No newline at end of file + /** + * + * + * @return ... + */ + public Object getValue(); + + /** + * + * + * @return ... + */ + public INode getNodeValue(); + + /** + * + * + * @return ... + */ + public String getStringValue(); + + /** + * + * + * @return ... + */ + public boolean getBooleanValue(); + + /** + * + * + * @return ... + */ + public long getIntegerValue(); + + /** + * + * + * @return ... + */ + public double getFloatValue(); + + /** + * + * + * @return ... + */ + public Date getDateValue(); + + /** + * + * + * @return ... + */ + public Object getJavaObjectValue(); +} diff --git a/src/helma/objectmodel/ITransaction.java b/src/helma/objectmodel/ITransaction.java index 1d617a9f..6455926d 100644 --- a/src/helma/objectmodel/ITransaction.java +++ b/src/helma/objectmodel/ITransaction.java @@ -1,18 +1,27 @@ -// ITransaction.java - +/* + * 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; + /** - * This interface is kept for databases that are able - * to run transactions. Transactions were used for the - * Berkeley database and might be used in other future - * databases, so we leave transactions in. - */ - + * This interface is kept for databases that are able + * to run transactions. Transactions were used for the + * Berkeley database and might be used in other future + * databases, so we leave transactions in. + */ public interface ITransaction { - } - - - - diff --git a/src/helma/objectmodel/NodeEvent.java b/src/helma/objectmodel/NodeEvent.java index b62bea67..d4e3f5a2 100644 --- a/src/helma/objectmodel/NodeEvent.java +++ b/src/helma/objectmodel/NodeEvent.java @@ -1,59 +1,92 @@ -// NodeEvent.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.objectmodel; import java.io.*; -/** +/** * This is passed to NodeListeners when a node is modified. */ - public class NodeEvent implements Serializable { - public static final int CONTENT_CHANGED = 0; public static final int PROPERTIES_CHANGED = 1; public static final int NODE_REMOVED = 2; public static final int NODE_RENAMED = 3; public static final int SUBNODE_ADDED = 4; public static final int SUBNODE_REMOVED = 5; - public int type; public String id; public transient INode node; public transient Object arg; - public NodeEvent (INode node, int type) { - super (); - this.node = node; - this.id = node.getID (); - this.type = type; + /** + * Creates a new NodeEvent object. + * + * @param node ... + * @param type ... + */ + public NodeEvent(INode node, int type) { + super(); + this.node = node; + this.id = node.getID(); + this.type = type; } - public NodeEvent (INode node, int type, Object arg) { - super (); - this.node = node; - this.id = node.getID (); - this.type = type; - this.arg = arg; + /** + * Creates a new NodeEvent object. + * + * @param node ... + * @param type ... + * @param arg ... + */ + public NodeEvent(INode node, int type, Object arg) { + super(); + this.node = node; + this.id = node.getID(); + this.type = type; + this.arg = arg; } - public String toString () { - switch (type) { - case CONTENT_CHANGED: - return "NodeEvent: content changed"; - case PROPERTIES_CHANGED: - return "NodeEvent: properties changed"; - case NODE_REMOVED: - return "NodeEvent: node removed"; - case NODE_RENAMED: - return "NodeEvent: node moved"; - case SUBNODE_ADDED: - return "NodeEvent: subnode added"; - case SUBNODE_REMOVED: - return "NodeEvent: subnode removed"; - } - return "NodeEvent: invalid type"; - } + /** + * + * + * @return ... + */ + public String toString() { + switch (type) { + case CONTENT_CHANGED: + return "NodeEvent: content changed"; -} \ No newline at end of file + case PROPERTIES_CHANGED: + return "NodeEvent: properties changed"; + + case NODE_REMOVED: + return "NodeEvent: node removed"; + + case NODE_RENAMED: + return "NodeEvent: node moved"; + + case SUBNODE_ADDED: + return "NodeEvent: subnode added"; + + case SUBNODE_REMOVED: + return "NodeEvent: subnode removed"; + } + + return "NodeEvent: invalid type"; + } +} diff --git a/src/helma/objectmodel/ObjectNotFoundException.java b/src/helma/objectmodel/ObjectNotFoundException.java index e58d19bb..81e44f94 100644 --- a/src/helma/objectmodel/ObjectNotFoundException.java +++ b/src/helma/objectmodel/ObjectNotFoundException.java @@ -1,6 +1,19 @@ -// ObjectNotFoundException.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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; @@ -8,42 +21,13 @@ package helma.objectmodel; * Thrown when an object could not found in the database where * it was expected. */ - public class ObjectNotFoundException extends Exception { - - public ObjectNotFoundException (String msg) { - super (msg); + /** + * Creates a new ObjectNotFoundException object. + * + * @param msg ... + */ + public ObjectNotFoundException(String msg) { + super(msg); } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/objectmodel/Property.java b/src/helma/objectmodel/Property.java index 5c8df5c7..3fca8396 100644 --- a/src/helma/objectmodel/Property.java +++ b/src/helma/objectmodel/Property.java @@ -1,195 +1,350 @@ -// Property.java -// Copyright (c) Hannes Wallnöfer 1997-2000 +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ package helma.objectmodel; import helma.util.*; -import java.util.Vector; -import java.util.Hashtable; -import java.util.Date; -import java.util.Enumeration; import java.io.*; import java.text.*; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; /** - * A property implementation for Nodes stored inside a database. + * A property implementation for Nodes stored inside a database. */ - public final class Property implements IProperty, Serializable { - - protected String propname; protected TransientNode node; - public String svalue; public boolean bvalue; public long lvalue; public double dvalue; public INode nvalue; public Object jvalue; - public int type; - - public Property (TransientNode node) { - this.node = node; + /** + * Creates a new Property object. + * + * @param node ... + */ + public Property(TransientNode node) { + this.node = node; } - public Property (String propname, TransientNode node) { - this.propname = propname; - this.node = node; + /** + * Creates a new Property object. + * + * @param propname ... + * @param node ... + */ + public Property(String propname, TransientNode node) { + this.propname = propname; + this.node = node; } - public String getName () { - return propname; + /** + * + * + * @return ... + */ + public String getName() { + return propname; } - public Object getValue () { - switch (type) { - case STRING: - return svalue; - case BOOLEAN: - return new Boolean (bvalue); - case INTEGER: - return new Long (lvalue); - case FLOAT: - return new Double (dvalue); - case DATE: - return new Date (lvalue); - case NODE: - return nvalue; - case JAVAOBJECT: - return jvalue; - } - return null; + /** + * + * + * @return ... + */ + public Object getValue() { + switch (type) { + case STRING: + return svalue; + + case BOOLEAN: + return new Boolean(bvalue); + + case INTEGER: + return new Long(lvalue); + + case FLOAT: + return new Double(dvalue); + + case DATE: + return new Date(lvalue); + + case NODE: + return nvalue; + + case JAVAOBJECT: + return jvalue; + } + + return null; } - public void setStringValue (String value) { - if (type == NODE) - this.nvalue = null; - if (type == JAVAOBJECT) - this.jvalue = null; - type = STRING; - this.svalue = value; + /** + * + * + * @param value ... + */ + public void setStringValue(String value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = STRING; + this.svalue = value; } - public void setIntegerValue (long value) { - if (type == NODE) - this.nvalue = null; - if (type == JAVAOBJECT) - this.jvalue = null; - type = INTEGER; - this.lvalue = value; + /** + * + * + * @param value ... + */ + public void setIntegerValue(long value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = INTEGER; + this.lvalue = value; } - public void setFloatValue (double value) { - if (type == NODE) - this.nvalue = null; - if (type == JAVAOBJECT) - this.jvalue = null; - type = FLOAT; - this.dvalue = value; + /** + * + * + * @param value ... + */ + public void setFloatValue(double value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = FLOAT; + this.dvalue = value; } - public void setDateValue (Date value) { - if (type == NODE) - this.nvalue = null; - if (type == JAVAOBJECT) - this.jvalue = null; - type = DATE; - this.lvalue = value.getTime(); + /** + * + * + * @param value ... + */ + public void setDateValue(Date value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = DATE; + this.lvalue = value.getTime(); } - public void setBooleanValue (boolean value) { - if (type == NODE) - this.nvalue = null; - if (type == JAVAOBJECT) - this.jvalue = null; - type = BOOLEAN; - this.bvalue = value; + /** + * + * + * @param value ... + */ + public void setBooleanValue(boolean value) { + if (type == NODE) { + this.nvalue = null; + } + + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = BOOLEAN; + this.bvalue = value; } - public void setNodeValue (INode value) { - if (type == JAVAOBJECT) - this.jvalue = null; - type = NODE; - this.nvalue = value; + /** + * + * + * @param value ... + */ + public void setNodeValue(INode value) { + if (type == JAVAOBJECT) { + this.jvalue = null; + } + + type = NODE; + this.nvalue = value; } + /** + * + * + * @param value ... + */ + public void setJavaObjectValue(Object value) { + if (type == NODE) { + this.nvalue = null; + } - public void setJavaObjectValue (Object value) { - if (type == NODE) - this.nvalue = null; - type = JAVAOBJECT; - this.jvalue = value; + type = JAVAOBJECT; + this.jvalue = value; } + /** + * + * + * @return ... + */ + public String getStringValue() { + switch (type) { + case STRING: + return svalue; - public String getStringValue () { - switch (type) { - case STRING: - return svalue; - case BOOLEAN: - return "" + bvalue; - case DATE: - SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy HH:mm"); - return format.format (new Date (lvalue)); - case INTEGER: - return Long.toString (lvalue); - case FLOAT: - return Double.toString (dvalue); - case NODE: - return nvalue.getName (); - case JAVAOBJECT: - return jvalue == null ? null : jvalue.toString (); - } - return ""; + case BOOLEAN: + return "" + bvalue; + + case DATE: + + SimpleDateFormat format = new SimpleDateFormat("dd.MM.yy HH:mm"); + + return format.format(new Date(lvalue)); + + case INTEGER: + return Long.toString(lvalue); + + case FLOAT: + return Double.toString(dvalue); + + case NODE: + return nvalue.getName(); + + case JAVAOBJECT: + return (jvalue == null) ? null : jvalue.toString(); + } + + return ""; } - public String toString () { - return getStringValue (); + /** + * + * + * @return ... + */ + public String toString() { + return getStringValue(); } - public long getIntegerValue () { - if (type == INTEGER) - return lvalue; - return 0; + /** + * + * + * @return ... + */ + public long getIntegerValue() { + if (type == INTEGER) { + return lvalue; + } + + return 0; } - public double getFloatValue () { - if (type == FLOAT) - return dvalue; - return 0.0; + /** + * + * + * @return ... + */ + public double getFloatValue() { + if (type == FLOAT) { + return dvalue; + } + + return 0.0; } + /** + * + * + * @return ... + */ + public Date getDateValue() { + if (type == DATE) { + return new Date(lvalue); + } - public Date getDateValue () { - if (type == DATE) - return new Date (lvalue); - return null; + return null; } - public boolean getBooleanValue () { - if (type == BOOLEAN) - return bvalue; - return false; + /** + * + * + * @return ... + */ + public boolean getBooleanValue() { + if (type == BOOLEAN) { + return bvalue; + } + + return false; } - public INode getNodeValue () { - if (type == NODE) - return nvalue; - return null; + /** + * + * + * @return ... + */ + public INode getNodeValue() { + if (type == NODE) { + return nvalue; + } + + return null; } - public Object getJavaObjectValue () { - if (type == JAVAOBJECT) - return jvalue; - return null; + /** + * + * + * @return ... + */ + public Object getJavaObjectValue() { + if (type == JAVAOBJECT) { + return jvalue; + } + + return null; } - - public int getType () { - return type; + /** + * + * + * @return ... + */ + public int getType() { + return type; } - } diff --git a/src/helma/objectmodel/TransientNode.java b/src/helma/objectmodel/TransientNode.java index 2c453e0e..6ef62cd1 100644 --- a/src/helma/objectmodel/TransientNode.java +++ b/src/helma/objectmodel/TransientNode.java @@ -1,588 +1,977 @@ -// TransientNode.java -// Copyright (c) Hannes Wallnöfer 1997-2000 - +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.objectmodel; - -import java.util.Vector; -import java.util.Hashtable; -import java.util.Enumeration; -import java.util.Date; -import java.util.StringTokenizer; -import java.io.*; -import helma.util.*; import helma.framework.IPathElement; import helma.objectmodel.db.DbMapping; import helma.objectmodel.db.Relation; +import helma.util.*; +import java.io.*; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; +import java.util.Vector; /** * A transient implementation of INode. An instance of this class can't be * made persistent by reachability from a persistent node. To make a persistent-capable * object, class helma.objectmodel.db.Node has to be used. */ - public class TransientNode implements INode, Serializable { - - - protected Hashtable propMap, nodeMap; + private static long idgen = 0; + protected Hashtable propMap; + protected Hashtable nodeMap; protected Vector nodes; protected TransientNode parent; - protected Vector links; // links to this node - protected Vector proplinks; // nodes using this node as property - + protected Vector links; // links to this node + protected Vector proplinks; // nodes using this node as property transient String prototype; - protected long created; protected long lastmodified; + protected String id; + protected String name; - protected String id, name; // is the main identity a named property or an anonymous node in a collection? protected boolean anonymous = false; - transient DbMapping dbmap; + INode cacheNode; - private static long idgen = 0; - - public static String generateID () { - // make transient ids differ from persistent ones - // and are unique within on runtime session - return "t"+idgen++; - } - - public TransientNode () { - id = generateID (); - name = id; - created = lastmodified = System.currentTimeMillis (); + /** + * Creates a new TransientNode object. + */ + public TransientNode() { + id = generateID(); + name = id; + created = lastmodified = System.currentTimeMillis(); } /** * Make a new TransientNode object with a given name */ - public TransientNode (String n) { - id = generateID (); - name = n == null || "".equals (n) ? id : n; - created = lastmodified = System.currentTimeMillis (); + public TransientNode(String n) { + id = generateID(); + name = ((n == null) || "".equals(n)) ? id : n; + created = lastmodified = System.currentTimeMillis(); } - - public void setDbMapping (DbMapping dbmap) { - this.dbmap = dbmap; + /** + * + * + * @return ... + */ + public static String generateID() { + // make transient ids differ from persistent ones + // and are unique within on runtime session + return "t" + idgen++; } - public DbMapping getDbMapping () { - return dbmap; + /** + * + * + * @param dbmap ... + */ + public void setDbMapping(DbMapping dbmap) { + this.dbmap = dbmap; } + /** + * + * + * @return ... + */ + public DbMapping getDbMapping() { + return dbmap; + } - /** + /** * navigation-related */ - - public String getID () { - return id; - } - - public boolean isAnonymous () { - return anonymous; - } - - - public String getName () { - return name; - } - - public String getElementName () { - return anonymous ? id : name; + public String getID() { + return id; } - public int getState () { - return TRANSIENT; + /** + * + * + * @return ... + */ + public boolean isAnonymous() { + return anonymous; } - public void setState (int s) { - // state always is TRANSIENT on this kind of node + /** + * + * + * @return ... + */ + public String getName() { + return name; } - public String getFullName () { - return getFullName (null); + /** + * + * + * @return ... + */ + public String getElementName() { + return anonymous ? id : name; } - public String getFullName (INode root) { - String fullname = ""; - String divider = null; - StringBuffer b = new StringBuffer (); - TransientNode p = this; - while (p != null && p.parent != null && p != root) { - if (divider != null) - b.insert (0, divider); - else - divider = "/"; - b.insert (0, p.getElementName ()); - p = p.parent; - } - return b.toString (); + /** + * + * + * @return ... + */ + public int getState() { + return TRANSIENT; } - - public void setName (String name) { - // if (name.indexOf('/') > -1) - // throw new RuntimeException ("The name of the node must not contain \"/\"."); - if (name == null || name.trim().length() == 0) - this.name = id; - else - this.name = name; + /** + * + * + * @param s ... + */ + public void setState(int s) { + // state always is TRANSIENT on this kind of node } - public String getPrototype () { - // if prototype is null, it's a vanilla HopObject. - if (prototype == null) - return "hopobject"; - return prototype; + /** + * + * + * @return ... + */ + public String getFullName() { + return getFullName(null); } - public void setPrototype (String proto) { - this.prototype = proto; + /** + * + * + * @param root ... + * + * @return ... + */ + public String getFullName(INode root) { + String fullname = ""; + String divider = null; + StringBuffer b = new StringBuffer(); + TransientNode p = this; + + while ((p != null) && (p.parent != null) && (p != root)) { + if (divider != null) { + b.insert(0, divider); + } else { + divider = "/"; + } + + b.insert(0, p.getElementName()); + p = p.parent; + } + + return b.toString(); } - - public INode getParent () { - return parent; + /** + * + * + * @param name ... + */ + public void setName(String name) { + // if (name.indexOf('/') > -1) + // throw new RuntimeException ("The name of the node must not contain \"/\"."); + if ((name == null) || (name.trim().length() == 0)) { + this.name = id; + } else { + this.name = name; + } } + /** + * + * + * @return ... + */ + public String getPrototype() { + // if prototype is null, it's a vanilla HopObject. + if (prototype == null) { + return "hopobject"; + } + + return prototype; + } + + /** + * + * + * @param proto ... + */ + public void setPrototype(String proto) { + this.prototype = proto; + } + + /** + * + * + * @return ... + */ + public INode getParent() { + return parent; + } /** * INode-related */ - - public void setSubnodeRelation (String rel) { - throw new RuntimeException ("Can't set subnode relation for non-persistent Node."); + public void setSubnodeRelation(String rel) { + throw new RuntimeException("Can't set subnode relation for non-persistent Node."); } - public String getSubnodeRelation () { - return null; + /** + * + * + * @return ... + */ + public String getSubnodeRelation() { + return null; } - public int numberOfNodes () { - return nodes == null ? 0 : nodes.size (); + /** + * + * + * @return ... + */ + public int numberOfNodes() { + return (nodes == null) ? 0 : nodes.size(); } - public INode addNode (INode elem) { - return addNode (elem, numberOfNodes ()); + /** + * + * + * @param elem ... + * + * @return ... + */ + public INode addNode(INode elem) { + return addNode(elem, numberOfNodes()); } - public INode addNode (INode elem, int where) { + /** + * + * + * @param elem ... + * @param where ... + * + * @return ... + */ + public INode addNode(INode elem, int where) { + if ((where < 0) || (where > numberOfNodes())) { + where = numberOfNodes(); + } - if (where < 0 || where > numberOfNodes ()) - where = numberOfNodes (); + String n = elem.getName(); - String n = elem.getName(); - if (n.indexOf('/') > -1) - throw new RuntimeException ("The name of a node must not contain \"/\" (slash)."); - - // IServer.getLogger().log ("adding: "+node+" -- "+node.getContentLength ()); - if (nodeMap != null && nodeMap.get (elem.getID ()) != null) { - nodes.removeElement (elem); - where = Math.min (where, numberOfNodes ()); - nodes.insertElementAt (elem, where); - return elem; - } + if (n.indexOf('/') > -1) { + throw new RuntimeException("The name of a node must not contain \"/\" (slash)."); + } - if (nodeMap == null) nodeMap = new Hashtable (); - if (nodes == null) nodes = new Vector (); + // IServer.getLogger().log ("adding: "+node+" -- "+node.getContentLength ()); + if ((nodeMap != null) && (nodeMap.get(elem.getID()) != null)) { + nodes.removeElement(elem); + where = Math.min(where, numberOfNodes()); + nodes.insertElementAt(elem, where); - nodeMap.put (elem.getID (), elem); - nodes.insertElementAt (elem, where); + return elem; + } - if (elem instanceof TransientNode) { - TransientNode node = (TransientNode) elem; - if (node.parent == null) { - node.parent = this; - node.anonymous = true; - } - } - - lastmodified = System.currentTimeMillis (); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, node)); - return elem; + if (nodeMap == null) { + nodeMap = new Hashtable(); + } + + if (nodes == null) { + nodes = new Vector(); + } + + nodeMap.put(elem.getID(), elem); + nodes.insertElementAt(elem, where); + + if (elem instanceof TransientNode) { + TransientNode node = (TransientNode) elem; + + if (node.parent == null) { + node.parent = this; + node.anonymous = true; + } + } + + lastmodified = System.currentTimeMillis(); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, node)); + return elem; } - public INode createNode () { - return createNode (null, 0); // where is ignored since this is an anonymous node + /** + * + * + * @return ... + */ + public INode createNode() { + return createNode(null, 0); // where is ignored since this is an anonymous node } - public INode createNode (int where) { - return createNode (null, where); + /** + * + * + * @param where ... + * + * @return ... + */ + public INode createNode(int where) { + return createNode(null, where); } - public INode createNode (String nm) { - return createNode (nm, numberOfNodes ()); // where is usually ignored (if nm != null) + /** + * + * + * @param nm ... + * + * @return ... + */ + public INode createNode(String nm) { + return createNode(nm, numberOfNodes()); // where is usually ignored (if nm != null) } - public INode createNode (String nm, int where) { - boolean anon = false; - if (nm == null || "".equals (nm.trim ())) - anon = true; - INode n = new TransientNode (nm); - if (anon) - addNode (n, where); - else - setNode (nm, n); - return n; - } + /** + * + * + * @param nm ... + * @param where ... + * + * @return ... + */ + public INode createNode(String nm, int where) { + boolean anon = false; + if ((nm == null) || "".equals(nm.trim())) { + anon = true; + } + + INode n = new TransientNode(nm); + + if (anon) { + addNode(n, where); + } else { + setNode(nm, n); + } + + return n; + } /** * register a node that links to this node. */ + /* protected void registerLink (TransientNode from) { - if (links == null) - links = new Vector (); - if (!links.contains (from)) - links.addElement (from); - } */ - - public IPathElement getParentElement () { - return getParent (); - } - - public IPathElement getChildElement (String name) { - return getNode (name); - } - - public INode getSubnode (String name) { - StringTokenizer st = new StringTokenizer (name, "/"); - TransientNode retval = this, runner; - while (st.hasMoreTokens () && retval != null) { - runner = retval; - String next = st.nextToken().trim().toLowerCase (); - if ("".equals (next)) - retval = this; - else - retval = runner.nodeMap == null ? null : (TransientNode) runner.nodeMap.get (next); - if (retval == null) - retval = (TransientNode) runner.getNode (next); - } - return retval; - } - - - public INode getSubnodeAt (int index) { - return nodes == null ? null : (INode) nodes.elementAt (index); - } - - public int contains (INode n) { - if (n == null || nodes == null) - return -1; - return nodes.indexOf (n); - } - - public boolean remove () { - if (anonymous) - parent.unset (name); - else - parent.removeNode (this); - return true; - } - - - public void removeNode (INode node) { - // IServer.getLogger().log ("removing: "+ node); - releaseNode (node); - TransientNode n = (TransientNode) node; - if (n.getParent () == this && n.anonymous) { - int l = n.links == null ? 0 : n.links.size (); // notify nodes that link to n that n is going down. - for (int i = 0; i < l; i++) { - TransientNode link = (TransientNode) n.links.elementAt (i); - link.releaseNode (n); - } - if (n.proplinks != null) { - // clean up all nodes that use n as a property - for (Enumeration e1 = n.proplinks.elements (); e1.hasMoreElements (); ) try { - Property p = (Property) e1.nextElement (); - p.node.propMap.remove (p.propname.toLowerCase ()); - } catch (Exception ignore) {} - } - // remove all subnodes, giving them a chance to destroy themselves. - Vector v = new Vector (); // removeElement modifies the Vector we are enumerating, so we are extra careful. - for (Enumeration e3 = n.getSubnodes (); e3.hasMoreElements (); ) { - v.addElement (e3.nextElement ()); - } - int m = v.size (); - for (int i=0; i -1 && nodes.elementAt (runner) != node) - runner = nodes.indexOf (node, Math.min (nodes.size()-1, runner+1)); - if (runner > -1) - nodes.removeElementAt (runner); - // nodes.remove (node); - Object what = nodeMap.remove (node.getName ().toLowerCase ()); - // Server.throwNodeEvent (new NodeEvent (node, NodeEvent.NODE_REMOVED)); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_REMOVED, node)); - lastmodified = System.currentTimeMillis (); - // IServer.getLogger().log ("released node "+node +" from "+this+" oldobj = "+what); + return; + } + + int runner = nodes.indexOf(node); + + // this is due to difference between .equals() and == + while ((runner > -1) && (nodes.elementAt(runner) != node)) + runner = nodes.indexOf(node, Math.min(nodes.size() - 1, runner + 1)); + + if (runner > -1) { + nodes.removeElementAt(runner); + } + + // nodes.remove (node); + Object what = nodeMap.remove(node.getName().toLowerCase()); + + // Server.throwNodeEvent (new NodeEvent (node, NodeEvent.NODE_REMOVED)); + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_REMOVED, node)); + lastmodified = System.currentTimeMillis(); + + // IServer.getLogger().log ("released node "+node +" from "+this+" oldobj = "+what); } - public Enumeration getSubnodes () { - return nodes == null ? new Vector ().elements () : nodes.elements (); + /** + * + * + * @return ... + */ + public Enumeration getSubnodes() { + return (nodes == null) ? new Vector().elements() : nodes.elements(); } - /** * property-related - */ - - public Enumeration properties () { - return propMap == null ? new EmptyEnumeration () : propMap.keys (); + */ + public Enumeration properties() { + return (propMap == null) ? new EmptyEnumeration() : propMap.keys(); } + private Property getProperty(String propname) { + Property prop = (propMap == null) ? null : (Property) propMap.get(propname); - private Property getProperty (String propname) { - Property prop = propMap == null ? null : (Property) propMap.get (propname); - // check if we have to create a virtual node - if (prop == null && dbmap != null) { - Relation rel = dbmap.getPropertyRelation (propname); - if (rel != null && rel.isVirtual ()) { - prop = makeVirtualNode (propname, rel); - } - } - return prop; + // check if we have to create a virtual node + if ((prop == null) && (dbmap != null)) { + Relation rel = dbmap.getPropertyRelation(propname); + + if ((rel != null) && rel.isVirtual()) { + prop = makeVirtualNode(propname, rel); + } + } + + return prop; } - private Property makeVirtualNode (String propname, Relation rel) { - INode node = new helma.objectmodel.db.Node (rel.getPropName (), rel.getPrototype (), dbmap.getWrappedNodeManager()); - // node.setState (TRANSIENT); - // make a db mapping good enough that the virtual node finds its subnodes - // DbMapping dbm = new DbMapping (); - // dbm.setSubnodeRelation (rel); - // dbm.setPropertyRelation (rel); - node.setDbMapping (rel.getVirtualMapping ()); - setNode (propname, node); - return (Property) propMap.get (propname); + private Property makeVirtualNode(String propname, Relation rel) { + INode node = new helma.objectmodel.db.Node(rel.getPropName(), rel.getPrototype(), + dbmap.getWrappedNodeManager()); + + // node.setState (TRANSIENT); + // make a db mapping good enough that the virtual node finds its subnodes + // DbMapping dbm = new DbMapping (); + // dbm.setSubnodeRelation (rel); + // dbm.setPropertyRelation (rel); + node.setDbMapping(rel.getVirtualMapping()); + setNode(propname, node); + + return (Property) propMap.get(propname); } + /** + * + * + * @param propname ... + * + * @return ... + */ + public IProperty get(String propname) { + propname = propname.toLowerCase(); - public IProperty get (String propname) { - propname = propname.toLowerCase (); - return getProperty (propname); + return getProperty(propname); } - public String getString (String propname, String defaultValue) { - String propValue = getString (propname); - return propValue == null ? defaultValue : propValue; + /** + * + * + * @param propname ... + * @param defaultValue ... + * + * @return ... + */ + public String getString(String propname, String defaultValue) { + String propValue = getString(propname); + + return (propValue == null) ? defaultValue : propValue; } - public String getString (String propname) { - propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getStringValue (); - } catch (Exception ignore) {} - return null; + /** + * + * + * @param propname ... + * + * @return ... + */ + public String getString(String propname) { + propname = propname.toLowerCase(); + + Property prop = getProperty(propname); + + try { + return prop.getStringValue(); + } catch (Exception ignore) { + } + + return null; } - public long getInteger (String propname) { - propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getIntegerValue (); - } catch (Exception ignore) {} - return 0; + /** + * + * + * @param propname ... + * + * @return ... + */ + public long getInteger(String propname) { + propname = propname.toLowerCase(); + + Property prop = getProperty(propname); + + try { + return prop.getIntegerValue(); + } catch (Exception ignore) { + } + + return 0; } - public double getFloat (String propname) { - propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getFloatValue (); - } catch (Exception ignore) {} - return 0.0; + /** + * + * + * @param propname ... + * + * @return ... + */ + public double getFloat(String propname) { + propname = propname.toLowerCase(); + + Property prop = getProperty(propname); + + try { + return prop.getFloatValue(); + } catch (Exception ignore) { + } + + return 0.0; } - public Date getDate (String propname) { - propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getDateValue (); - } catch (Exception ignore) {} - return null; + /** + * + * + * @param propname ... + * + * @return ... + */ + public Date getDate(String propname) { + propname = propname.toLowerCase(); + + Property prop = getProperty(propname); + + try { + return prop.getDateValue(); + } catch (Exception ignore) { + } + + return null; } + /** + * + * + * @param propname ... + * + * @return ... + */ + public boolean getBoolean(String propname) { + propname = propname.toLowerCase(); - public boolean getBoolean (String propname) { - propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getBooleanValue (); - } catch (Exception ignore) {} - return false; + Property prop = getProperty(propname); + + try { + return prop.getBooleanValue(); + } catch (Exception ignore) { + } + + return false; } - public INode getNode (String propname) { - propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getNodeValue (); - } catch (Exception ignore) {} - return null; + /** + * + * + * @param propname ... + * + * @return ... + */ + public INode getNode(String propname) { + propname = propname.toLowerCase(); + + Property prop = getProperty(propname); + + try { + return prop.getNodeValue(); + } catch (Exception ignore) { + } + + return null; } - public Object getJavaObject (String propname) { - propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getJavaObjectValue (); - } catch (Exception ignore) {} - return null; + /** + * + * + * @param propname ... + * + * @return ... + */ + public Object getJavaObject(String propname) { + propname = propname.toLowerCase(); + + Property prop = getProperty(propname); + + try { + return prop.getJavaObjectValue(); + } catch (Exception ignore) { + } + + return null; } // create a property if it doesn't exist for this name - private Property initProperty (String propname) { - if (propMap == null) - propMap = new Hashtable (); - propname = propname.trim (); - String p2 = propname.toLowerCase (); - Property prop = (Property) propMap.get (p2); - if (prop == null) { - prop = new Property (propname, this); - propMap.put (p2, prop); - } - return prop; + private Property initProperty(String propname) { + if (propMap == null) { + propMap = new Hashtable(); + } + + propname = propname.trim(); + + String p2 = propname.toLowerCase(); + Property prop = (Property) propMap.get(p2); + + if (prop == null) { + prop = new Property(propname, this); + propMap.put(p2, prop); + } + + return prop; } - public void setString (String propname, String value) { - // IServer.getLogger().log ("setting String prop"); - Property prop = initProperty (propname); - prop.setStringValue (value); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setString(String propname, String value) { + // IServer.getLogger().log ("setting String prop"); + Property prop = initProperty(propname); + + prop.setStringValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); } - public void setInteger (String propname, long value) { - // IServer.getLogger().log ("setting bool prop"); - Property prop = initProperty (propname); - prop.setIntegerValue (value); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setInteger(String propname, long value) { + // IServer.getLogger().log ("setting bool prop"); + Property prop = initProperty(propname); + + prop.setIntegerValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); } - public void setFloat (String propname, double value) { - // IServer.getLogger().log ("setting bool prop"); - Property prop = initProperty (propname); - prop.setFloatValue (value); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setFloat(String propname, double value) { + // IServer.getLogger().log ("setting bool prop"); + Property prop = initProperty(propname); + + prop.setFloatValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); } - public void setBoolean (String propname, boolean value) { - // IServer.getLogger().log ("setting bool prop"); - Property prop = initProperty (propname); - prop.setBooleanValue (value); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setBoolean(String propname, boolean value) { + // IServer.getLogger().log ("setting bool prop"); + Property prop = initProperty(propname); + + prop.setBooleanValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); } + /** + * + * + * @param propname ... + * @param value ... + */ + public void setDate(String propname, Date value) { + // IServer.getLogger().log ("setting date prop"); + Property prop = initProperty(propname); - public void setDate (String propname, Date value) { - // IServer.getLogger().log ("setting date prop"); - Property prop = initProperty (propname); - prop.setDateValue (value); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); + prop.setDateValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); } - public void setJavaObject (String propname, Object value) { - // IServer.getLogger().log ("setting date prop"); - Property prop = initProperty (propname); - prop.setJavaObjectValue (value); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setJavaObject(String propname, Object value) { + // IServer.getLogger().log ("setting date prop"); + Property prop = initProperty(propname); + + prop.setJavaObjectValue(value); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); } - public void setNode (String propname, INode value) { - // IServer.getLogger().log ("setting date prop"); - Property prop = initProperty (propname); - prop.setNodeValue (value); - - // check if the main identity of this node is as a named property - // or as an anonymous node in a collection - if (value instanceof TransientNode) { - TransientNode n = (TransientNode) value; - if (n.parent == null) { - n.name = propname; - n.parent = this; - n.anonymous = false; - } - } - - lastmodified = System.currentTimeMillis (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setNode(String propname, INode value) { + // IServer.getLogger().log ("setting date prop"); + Property prop = initProperty(propname); + + prop.setNodeValue(value); + + // check if the main identity of this node is as a named property + // or as an anonymous node in a collection + if (value instanceof TransientNode) { + TransientNode n = (TransientNode) value; + + if (n.parent == null) { + n.name = propname; + n.parent = this; + n.anonymous = false; + } + } + + lastmodified = System.currentTimeMillis(); } - public void unset (String propname) { - if (propMap == null) - return; - try { - Property p = (Property) propMap.remove (propname.toLowerCase ()); - lastmodified = System.currentTimeMillis (); - } catch (Exception ignore) {} - } + /** + * + * + * @param propname ... + */ + public void unset(String propname) { + if (propMap == null) { + return; + } + try { + Property p = (Property) propMap.remove(propname.toLowerCase()); + + lastmodified = System.currentTimeMillis(); + } catch (Exception ignore) { + } + } /* public String getUrl (INode root, INode users, String tmpname, String rootproto) { - throw new RuntimeException ("HREFs on transient (non-db based) Nodes not supported"); - } */ - - - public long lastModified () { - return lastmodified; + throw new RuntimeException ("HREFs on transient (non-db based) Nodes not supported"); + } */ + public long lastModified() { + return lastmodified; } - public long created () { - return created; + /** + * + * + * @return ... + */ + public long created() { + return created; } - public String toString () { - return "TransientNode " + name; + /** + * + * + * @return ... + */ + public String toString() { + return "TransientNode " + name; } - - INode cacheNode; /** * Get the cache node for this node. This can * be used to store transient cache data per node * from Javascript. */ - public synchronized INode getCacheNode () { - if (cacheNode == null) - cacheNode = new TransientNode(); - return cacheNode; + public synchronized INode getCacheNode() { + if (cacheNode == null) { + cacheNode = new TransientNode(); + } + + return cacheNode; } /** * Reset the cache node for this node. */ - public synchronized void clearCacheNode () { - cacheNode = null; + public synchronized void clearCacheNode() { + cacheNode = null; } - } - - - - diff --git a/src/helma/objectmodel/db/DbColumn.java b/src/helma/objectmodel/db/DbColumn.java index df98a7bd..51c15b16 100644 --- a/src/helma/objectmodel/db/DbColumn.java +++ b/src/helma/objectmodel/db/DbColumn.java @@ -1,48 +1,62 @@ -// DbColumn.java -// Copyright 2002 Hannes Wallnoefer, Helma.org +/* + * 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; -/** - * A class that encapsulates the Column name and data type of a + +/** + * A class that encapsulates the Column name and data type of a * column in a relational table. */ public final class DbColumn { - private final String name; private final int type; private final Relation relation; - + /** * Constructor */ - public DbColumn (String name, int type, Relation rel) { - this.name = name; - this.type = type; - this.relation = rel; - if (relation != null) - relation.setColumnType (type); + public DbColumn(String name, int type, Relation rel) { + this.name = name; + this.type = type; + this.relation = rel; + + if (relation != null) { + relation.setColumnType(type); + } } /** * Get the column name. */ public String getName() { - return name; + return name; } /** * Get this columns SQL data type. */ public int getType() { - return type; + return type; } /** * Return the relation associated with this column. May be null. */ public Relation getRelation() { - return relation; + return relation; } - } diff --git a/src/helma/objectmodel/db/DbKey.java b/src/helma/objectmodel/db/DbKey.java index 6043f5e8..8f947bbb 100644 --- a/src/helma/objectmodel/db/DbKey.java +++ b/src/helma/objectmodel/db/DbKey.java @@ -1,22 +1,34 @@ -// DbKey.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * 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; - /** * This is the internal representation of a database key. It is constructed * from the logical table (type) name and the object's primary key * within the table. Currently only single keys are supported. */ public final class DbKey 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 String id; @@ -26,50 +38,82 @@ public final class DbKey implements Key, Serializable { /** * make a key for a persistent Object, describing its datasource and id. */ - public DbKey (DbMapping dbmap, String id) { - this.id = id; - this.storageName = dbmap == null ? null : dbmap.getStorageTypeName (); - } - - - - public boolean equals (Object what) { - if (what == this) - return true; - if (!(what instanceof DbKey)) - return false; - DbKey k = (DbKey) 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 && (id == k.id || id.equals (k.id)); + public DbKey(DbMapping dbmap, String id) { + this.id = id; + this.storageName = (dbmap == null) ? null : dbmap.getStorageTypeName(); } - public int hashCode () { - if (hashcode == 0) { - hashcode = storageName == null ? - 17 + 37*id.hashCode () : - 17 + 37*storageName.hashCode() + +37*id.hashCode (); - } - return hashcode; + /** + * + * + * @param what ... + * + * @return ... + */ + public boolean equals(Object what) { + if (what == this) { + return true; + } + + if (!(what instanceof DbKey)) { + return false; + } + + DbKey k = (DbKey) 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) && ((id == k.id) || id.equals(k.id)); } - public Key getParentKey () { - return null; + /** + * + * + * @return ... + */ + public int hashCode() { + if (hashcode == 0) { + hashcode = (storageName == null) ? (17 + (37 * id.hashCode())) + : (17 + (37 * storageName.hashCode()) + + (+37 * id.hashCode())); + } + + return hashcode; } - public String getStorageName () { - return storageName; + /** + * + * + * @return ... + */ + public Key getParentKey() { + return null; } - public String getID () { - return id; + /** + * + * + * @return ... + */ + public String getStorageName() { + return storageName; } - public String toString () { - return storageName == null ? "["+id+"]" : storageName+"["+id+"]"; + /** + * + * + * @return ... + */ + public String getID() { + return id; } - + /** + * + * + * @return ... + */ + public String toString() { + return (storageName == null) ? ("[" + id + "]") : (storageName + "[" + id + "]"); + } } - - diff --git a/src/helma/objectmodel/db/DbMapping.java b/src/helma/objectmodel/db/DbMapping.java index f8995ee8..a4487f8e 100644 --- a/src/helma/objectmodel/db/DbMapping.java +++ b/src/helma/objectmodel/db/DbMapping.java @@ -1,28 +1,40 @@ -// DbMapping.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * 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 helma.framework.core.Application; -import helma.util.Updatable; import helma.util.SystemProperties; +import helma.util.Updatable; +import java.sql.*; +import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; -import java.util.Enumeration; import java.util.Iterator; import java.util.StringTokenizer; -import java.sql.*; /** - * A DbMapping describes how a certain type of Nodes is to mapped to a - * relational database table. Basically it consists of a set of JavaScript property-to- - * Database row bindings which are represented by instances of the Relation class. - */ - + * A DbMapping describes how a certain type of Nodes is to mapped to a + * relational database table. Basically it consists of a set of JavaScript property-to- + * Database row bindings which are represented by instances of the Relation class. + */ public final class DbMapping implements Updatable { - // DbMappings belong to an application Application app; + // prototype name of this mapping String typename; @@ -31,8 +43,10 @@ public final class DbMapping implements Updatable { // name of data dbSource to which this mapping writes DbSource dbSource; + // name of datasource String dbSourceName; + // name of db table String tableName; @@ -47,8 +61,9 @@ public final class DbMapping implements Updatable { // we need a DbMapping for those groupby nodes DbMapping groupbyMapping; - // Map of property names to Relations objects + // Map of property names to Relations objects HashMap prop2db; + // Map of db columns to Relations objects. // Case insensitive, keys are stored in upper case so // lookups must do a toUpperCase(). @@ -56,6 +71,7 @@ public final class DbMapping implements Updatable { // list of columns to fetch from db DbColumn[] columns = null; + // Map of db columns by name HashMap columnMap; @@ -66,13 +82,16 @@ public final class DbMapping implements Updatable { // db field used as primary key private String idField; + // db field used as object name String nameField; + // db field used to identify name of prototype to use for object instantiation String protoField; // name of parent prototype, if any String extendsProto; + // dbmapping of parent prototype, if any DbMapping parentMapping; @@ -81,6 +100,7 @@ public final class DbMapping implements Updatable { // descriptor for key generation method private String idgen; + // remember last key generated for this table long lastID; @@ -88,483 +108,646 @@ public final class DbMapping implements Updatable { // init value is -1 so we know we have to run update once even if // the underlying properties file is non-existent long lastTypeChange = -1; + // timestamp of last modification of an object of this type long lastDataChange; /** * Create an empty DbMapping */ - public DbMapping (Application app) { + public DbMapping(Application app) { + this.app = app; + this.typename = null; - this.app = app; - this.typename = null; + prop2db = new HashMap(); + db2prop = new HashMap(); - prop2db = new HashMap (); - db2prop = new HashMap (); + parentInfo = null; - parentInfo = null; - - idField = null; + idField = null; } /** * Create a DbMapping from a type.properties property file */ - public DbMapping (Application app, String typename, SystemProperties props) { + public DbMapping(Application app, String typename, SystemProperties props) { + this.app = app; + this.typename = typename; - this.app = app; - this.typename = typename; - // create a unique instance of the string. This is useful so - // we can compare types just by using == instead of equals. - if (typename != null) - typename = typename.intern (); + // create a unique instance of the string. This is useful so + // we can compare types just by using == instead of equals. + if (typename != null) { + typename = typename.intern(); + } - prop2db = new HashMap (); - db2prop = new HashMap (); - - columnMap = new HashMap (); + prop2db = new HashMap(); + db2prop = new HashMap(); - parentInfo = null; + columnMap = new HashMap(); - idField = null; + parentInfo = null; - this.props = props; + idField = null; + + this.props = props; } /** * Tell the type manager whether we need update() to be called */ - public boolean needsUpdate () { - return props.lastModified () != lastTypeChange; + public boolean needsUpdate() { + return props.lastModified() != lastTypeChange; } - /** * 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 * for rewire to work, all other db mappings must have been initialized and registered. */ - public synchronized void update () { - // read in properties - tableName = props.getProperty ("_table"); - idgen = props.getProperty ("_idgen"); - // see if there is a field which specifies the prototype of objects, if different prototypes - // can be stored in this table - prototypeField = props.getProperty ("_prototypefield"); - // see if this prototype extends (inherits from) any other prototype - extendsProto = props.getProperty ("_extends"); + public synchronized void update() { + // read in properties + tableName = props.getProperty("_table"); + idgen = props.getProperty("_idgen"); - dbSourceName = props.getProperty ("_db"); - if (dbSourceName != null) { - dbSource = app.getDbSource (dbSourceName); - if (dbSource == null) { - app.logEvent ("*** Data Source for prototype "+typename+" does not exist: "+dbSourceName); - app.logEvent ("*** accessing or storing a "+typename+" object will cause an error."); - } else if (tableName == null) { - app.logEvent ("*** No table name specified for prototype "+typename); - app.logEvent ("*** accessing or storing a "+typename+" object will cause an error."); - // mark mapping as invalid by nulling the dbSource field - dbSource = null; - } - } + // see if there is a field which specifies the prototype of objects, if different prototypes + // can be stored in this table + prototypeField = props.getProperty("_prototypefield"); - // if id field is null, we assume "ID" as default. We don't set it - // however, so that if null we check the parent prototype first. - idField = props.getProperty ("_id"); + // see if this prototype extends (inherits from) any other prototype + extendsProto = props.getProperty("_extends"); - nameField = props.getProperty ("_name"); + dbSourceName = props.getProperty("_db"); - protoField = props.getProperty ("_prototype"); + if (dbSourceName != null) { + dbSource = app.getDbSource(dbSourceName); - String parentSpec = props.getProperty ("_parent"); - if (parentSpec != null) { - // comma-separated list of properties to be used as parent - StringTokenizer st = new StringTokenizer (parentSpec, ",;"); - parentInfo = new ParentInfo[st.countTokens()]; - for (int i=0; i "+dbField); - } - } catch (Exception x) { - app.logEvent ("Error in type.properties: "+x.getMessage ()); - } - } + if (parentSpec != null) { + // comma-separated list of properties to be used as parent + StringTokenizer st = new StringTokenizer(parentSpec, ",;"); - prop2db = p2d; - db2prop = d2p; + parentInfo = new ParentInfo[st.countTokens()]; - String subnodeMapping = props.getProperty ("_children"); - if (subnodeMapping != null) { - try { - // check if subnode relation already exists. If so, reuse it - if (subRelation == null) - subRelation = new Relation (subnodeMapping, "_children", this, props); - subRelation.update (subnodeMapping, props); - // if subnodes are accessed via access name or group name, - // the subnode relation is also the property relation. - if (subRelation.accessName != null || subRelation.groupby != null) - propRelation = subRelation; - } catch (Exception x) { - app.logEvent ("Error reading _subnodes relation for "+typename+": "+x.getMessage ()); - // subRelation = null; - } - } else { - subRelation = propRelation = null; - } + for (int i = 0; i < parentInfo.length; i++) + parentInfo[i] = new ParentInfo(st.nextToken().trim()); + } else { + parentInfo = null; + } - if (groupbyMapping != null) { - initGroupbyMapping (); - groupbyMapping.lastTypeChange = this.lastTypeChange; - } + lastTypeChange = props.lastModified(); + + // null the cached columns and select string + columns = null; + columnMap.clear(); + 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 d2p = new HashMap(); + + for (Enumeration e = props.keys(); e.hasMoreElements();) { + String propName = (String) e.nextElement(); + + try { + // ignore internal properties (starting with "_") and sub-options (containing a ".") + if (!propName.startsWith("_") && (propName.indexOf(".") < 0)) { + String dbField = props.getProperty(propName); + + // check if a relation for this propery already exists. If so, reuse it + Relation rel = (Relation) prop2db.get(propName.toLowerCase()); + + if (rel == null) { + rel = new Relation(dbField, propName, this, props); + } + + rel.update(dbField, props); + + // key enumerations from SystemProperties are all lower case, which is why + // even though we don't do a toLowerCase() here, + // we have to when we lookup things in p2d later. + p2d.put(propName, rel); + + if ((rel.columnName != null) && + ((rel.reftype == Relation.PRIMITIVE) || + (rel.reftype == Relation.REFERENCE))) { + d2p.put(rel.columnName.toUpperCase(), rel); + } + + // app.logEvent ("Mapping "+propName+" -> "+dbField); + } + } catch (Exception x) { + app.logEvent("Error in type.properties: " + x.getMessage()); + } + } + + prop2db = p2d; + db2prop = d2p; + + String subnodeMapping = props.getProperty("_children"); + + if (subnodeMapping != null) { + try { + // check if subnode relation already exists. If so, reuse it + if (subRelation == null) { + subRelation = new Relation(subnodeMapping, "_children", this, props); + } + + subRelation.update(subnodeMapping, props); + + // if subnodes are accessed via access name or group name, + // the subnode relation is also the property relation. + if ((subRelation.accessName != null) || (subRelation.groupby != null)) { + propRelation = subRelation; + } + } catch (Exception x) { + app.logEvent("Error reading _subnodes relation for " + typename + ": " + + x.getMessage()); + + // subRelation = null; + } + } else { + subRelation = propRelation = null; + } + + if (groupbyMapping != null) { + initGroupbyMapping(); + groupbyMapping.lastTypeChange = this.lastTypeChange; + } } - /** * Method in interface Updatable. */ - public void remove () { - // do nothing, removing of type properties is not implemented. + public void remove() { + // do nothing, removing of type properties is not implemented. } /** * Get a JDBC connection for this DbMapping. */ - public Connection getConnection () throws ClassNotFoundException, SQLException { - if (dbSourceName == null) { - if (parentMapping != null) - return parentMapping.getConnection (); - else - throw new SQLException ("Tried to get Connection from non-relational embedded data source."); - } - if (tableName == null) { - throw new SQLException ("Invalid DbMapping, _table not specified: "+this); - } - // if dbSource was previously not available, check again - if (dbSource == null) { - dbSource = app.getDbSource (dbSourceName); - } - if (dbSource == null) { - throw new SQLException ("Datasource is not defined: "+dbSourceName+"."); - } - return dbSource.getConnection (); + public Connection getConnection() throws ClassNotFoundException, SQLException { + if (dbSourceName == null) { + if (parentMapping != null) { + return parentMapping.getConnection(); + } else { + throw new SQLException("Tried to get Connection from non-relational embedded data source."); + } + } + + if (tableName == null) { + throw new SQLException("Invalid DbMapping, _table not specified: " + this); + } + + // if dbSource was previously not available, check again + if (dbSource == null) { + dbSource = app.getDbSource(dbSourceName); + } + + if (dbSource == null) { + throw new SQLException("Datasource is not defined: " + dbSourceName + "."); + } + + return dbSource.getConnection(); } /** * Get the DbSource object for this DbMapping. The DbSource describes a JDBC * data source including URL, JDBC driver, username and password. */ - public DbSource getDbSource () { - if (dbSource == null) { - if (tableName != null && dbSourceName != null) - dbSource = app.getDbSource (dbSourceName); - else if (parentMapping != null) - return parentMapping.getDbSource (); - } - return dbSource; - } + public DbSource getDbSource() { + if (dbSource == null) { + if ((tableName != null) && (dbSourceName != null)) { + dbSource = app.getDbSource(dbSourceName); + } else if (parentMapping != null) { + return parentMapping.getDbSource(); + } + } + return dbSource; + } /** * Get the table name used for this type mapping. */ - public String getTableName () { - if (tableName == null && parentMapping != null) - return parentMapping.getTableName (); - return tableName; + public String getTableName() { + if ((tableName == null) && (parentMapping != null)) { + return parentMapping.getTableName(); + } + + return tableName; } /** * Get the application this DbMapping belongs to. */ - public Application getApplication () { - return app; + public Application getApplication() { + return app; } /** * Get the name of this mapping's application */ - public String getAppName () { - return app.getName(); + public String getAppName() { + return app.getName(); } /** * Get the name of the object type this DbMapping belongs to. */ - public String getTypeName () { - return typename; + public String getTypeName() { + return typename; } /** * Get the name of this type's parent type, if any. */ - public String getExtends () { - return extendsProto; + public String getExtends() { + return extendsProto; } /** * Get the primary key column name for objects using this mapping. */ - public String getIDField () { - if (idField == null && parentMapping != null) - return parentMapping.getIDField (); - return idField == null ? "ID" : idField; + public String getIDField() { + if ((idField == null) && (parentMapping != null)) { + return parentMapping.getIDField(); + } + + return (idField == null) ? "ID" : idField; } /** * Get the column used for (internal) names of objects of this type. */ - public String getNameField () { - if (nameField == null && parentMapping != null) - return parentMapping.getNameField (); - return nameField; + public String getNameField() { + if ((nameField == null) && (parentMapping != null)) { + return parentMapping.getNameField(); + } + + return nameField; } /** * Get the column used for names of prototype. */ - public String getPrototypeField () { - if (protoField == null && parentMapping != null) - return parentMapping.getPrototypeField (); - return protoField; - } + public String getPrototypeField() { + if ((protoField == null) && (parentMapping != null)) { + return parentMapping.getPrototypeField(); + } - - /** - * Translate a database column name to an object property name according to this mapping. - */ - public String columnNameToProperty (String columnName) { - if (columnName == null) - return null; - return _columnNameToProperty (columnName.toUpperCase()); - } - - private String _columnNameToProperty (final String columnName) { - Relation rel = (Relation) db2prop.get (columnName); - if (rel == null && parentMapping != null) - return parentMapping._columnNameToProperty (columnName); - if (rel != null && (rel.reftype == Relation.PRIMITIVE || rel.reftype == Relation.REFERENCE)) - return rel.propName; - return null; - } - - /** - * Translate an object property name to a database column name according to this mapping. - */ - public String propertyToColumnName (String propName) { - if (propName == null) - return null; - // FIXME: prop2db stores keys in lower case, because it gets them - // from a SystemProperties object which converts keys to lower case. - return _propertyToColumnName (propName.toLowerCase ()); - } - - private String _propertyToColumnName (final String propName) { - Relation rel = (Relation) prop2db.get (propName); - if (rel == null && parentMapping != null) - return parentMapping._propertyToColumnName (propName); - if (rel != null && (rel.reftype == Relation.PRIMITIVE || rel.reftype == Relation.REFERENCE)) - return rel.columnName; - return null; + return protoField; } /** * Translate a database column name to an object property name according to this mapping. */ - public Relation columnNameToRelation (String columnName) { - if (columnName == null) - return null; - return _columnNameToRelation (columnName.toUpperCase()); + public String columnNameToProperty(String columnName) { + if (columnName == null) { + return null; + } + + return _columnNameToProperty(columnName.toUpperCase()); } - - private Relation _columnNameToRelation (final String columnName) { - Relation rel = (Relation) db2prop.get (columnName); - if (rel == null && parentMapping != null) - return parentMapping._columnNameToRelation (columnName); - return rel; + + private String _columnNameToProperty(final String columnName) { + Relation rel = (Relation) db2prop.get(columnName); + + if ((rel == null) && (parentMapping != null)) { + return parentMapping._columnNameToProperty(columnName); + } + + if ((rel != null) && + ((rel.reftype == Relation.PRIMITIVE) || + (rel.reftype == Relation.REFERENCE))) { + return rel.propName; + } + + return null; } /** * Translate an object property name to a database column name according to this mapping. */ - public Relation propertyToRelation (String propName) { - if (propName == null) - return null; - // FIXME: prop2db stores keys in lower case, because it gets them - // from a SystemProperties object which converts keys to lower case. - return _propertyToRelation (propName.toLowerCase()); + public String propertyToColumnName(String propName) { + if (propName == null) { + return null; + } + + // FIXME: prop2db stores keys in lower case, because it gets them + // from a SystemProperties object which converts keys to lower case. + return _propertyToColumnName(propName.toLowerCase()); } - private Relation _propertyToRelation (String propName) { - Relation rel = (Relation) prop2db.get (propName); - if (rel == null && parentMapping != null) - return parentMapping._propertyToRelation (propName); - return rel; + private String _propertyToColumnName(final String propName) { + Relation rel = (Relation) prop2db.get(propName); + + if ((rel == null) && (parentMapping != null)) { + return parentMapping._propertyToColumnName(propName); + } + + if ((rel != null) && + ((rel.reftype == Relation.PRIMITIVE) || + (rel.reftype == Relation.REFERENCE))) { + return rel.columnName; + } + + return null; } + /** + * Translate a database column name to an object property name according to this mapping. + */ + public Relation columnNameToRelation(String columnName) { + if (columnName == null) { + return null; + } + + return _columnNameToRelation(columnName.toUpperCase()); + } + + private Relation _columnNameToRelation(final String columnName) { + Relation rel = (Relation) db2prop.get(columnName); + + if ((rel == null) && (parentMapping != null)) { + return parentMapping._columnNameToRelation(columnName); + } + + return rel; + } + + /** + * Translate an object property name to a database column name according to this mapping. + */ + public Relation propertyToRelation(String propName) { + if (propName == null) { + return null; + } + + // FIXME: prop2db stores keys in lower case, because it gets them + // from a SystemProperties object which converts keys to lower case. + return _propertyToRelation(propName.toLowerCase()); + } + + private Relation _propertyToRelation(String propName) { + Relation rel = (Relation) prop2db.get(propName); + + if ((rel == null) && (parentMapping != null)) { + return parentMapping._propertyToRelation(propName); + } + + return rel; + } /** * This returns the parent info array, which tells an object of this type how to * determine its parent object. */ - public synchronized ParentInfo[] getParentInfo () { - if (parentInfo == null && parentMapping != null) - return parentMapping.getParentInfo (); - return parentInfo; + public synchronized ParentInfo[] getParentInfo() { + if ((parentInfo == null) && (parentMapping != null)) { + return parentMapping.getParentInfo(); + } + + return parentInfo; } + /** + * + * + * @return ... + */ + public DbMapping getSubnodeMapping() { + if (subRelation != null) { + return subRelation.otherType; + } - public DbMapping getSubnodeMapping () { - if (subRelation != null) - return subRelation.otherType; - if (parentMapping != null) - return parentMapping.getSubnodeMapping (); - return null; + if (parentMapping != null) { + return parentMapping.getSubnodeMapping(); + } + + return null; } + /** + * + * + * @param propname ... + * + * @return ... + */ + public DbMapping getExactPropertyMapping(String propname) { + Relation rel = getExactPropertyRelation(propname); - public DbMapping getExactPropertyMapping (String propname) { - Relation rel = getExactPropertyRelation (propname); - return rel != null ? rel.otherType : null; + return (rel != null) ? rel.otherType : null; } - public DbMapping getPropertyMapping (String propname) { - Relation rel = getPropertyRelation (propname); - if (rel != null) { - // if this is a virtual node, it doesn't have a dbmapping - if (rel.virtual && rel.prototype == null) - return null; - else - return rel.otherType; - } - return null; + /** + * + * + * @param propname ... + * + * @return ... + */ + public DbMapping getPropertyMapping(String propname) { + Relation rel = getPropertyRelation(propname); + + if (rel != null) { + // if this is a virtual node, it doesn't have a dbmapping + if (rel.virtual && (rel.prototype == null)) { + return null; + } else { + return rel.otherType; + } + } + + return null; } /** * If subnodes are grouped by one of their properties, return the * db-mapping with the right relations to create the group-by nodes */ - public synchronized DbMapping getGroupbyMapping () { - if (subRelation == null || subRelation.groupby == null) - return null; - if (groupbyMapping == null) { - initGroupbyMapping (); - } - return groupbyMapping; + public synchronized DbMapping getGroupbyMapping() { + if ((subRelation == null) || (subRelation.groupby == null)) { + return null; + } + + if (groupbyMapping == null) { + initGroupbyMapping(); + } + + return groupbyMapping; } /** * Initialize the dbmapping used for group-by nodes. */ - private void initGroupbyMapping () { - // if a prototype is defined for groupby nodes, use that - // if mapping doesn' exist or isn't defined, create a new (anonymous internal) one - groupbyMapping = new DbMapping (app); - // If a mapping is defined, make the internal mapping inherit from - // the defined named prototype. - if (subRelation.groupbyPrototype != null) - groupbyMapping.parentMapping = app.getDbMapping (subRelation.groupbyPrototype); - groupbyMapping.subRelation = subRelation.getGroupbySubnodeRelation (); - if (propRelation != null) - groupbyMapping.propRelation = propRelation.getGroupbyPropertyRelation (); - else - groupbyMapping.propRelation = subRelation.getGroupbyPropertyRelation (); - groupbyMapping.typename = subRelation.groupbyPrototype; + private void initGroupbyMapping() { + // if a prototype is defined for groupby nodes, use that + // if mapping doesn' exist or isn't defined, create a new (anonymous internal) one + groupbyMapping = new DbMapping(app); + + // If a mapping is defined, make the internal mapping inherit from + // the defined named prototype. + if (subRelation.groupbyPrototype != null) { + groupbyMapping.parentMapping = app.getDbMapping(subRelation.groupbyPrototype); + } + + groupbyMapping.subRelation = subRelation.getGroupbySubnodeRelation(); + + if (propRelation != null) { + groupbyMapping.propRelation = propRelation.getGroupbyPropertyRelation(); + } else { + groupbyMapping.propRelation = subRelation.getGroupbyPropertyRelation(); + } + + groupbyMapping.typename = subRelation.groupbyPrototype; } - - public void setPropertyRelation (Relation rel) { - propRelation = rel; + /** + * + * + * @param rel ... + */ + public void setPropertyRelation(Relation rel) { + propRelation = rel; } - public Relation getSubnodeRelation () { - if (subRelation == null && parentMapping != null) - return parentMapping.getSubnodeRelation (); - return subRelation; + /** + * + * + * @return ... + */ + public Relation getSubnodeRelation() { + if ((subRelation == null) && (parentMapping != null)) { + return parentMapping.getSubnodeRelation(); + } + + return subRelation; } - public Relation getPropertyRelation () { - if (propRelation == null && parentMapping != null) - return parentMapping.getPropertyRelation (); - return propRelation; + /** + * + * + * @return ... + */ + public Relation getPropertyRelation() { + if ((propRelation == null) && (parentMapping != null)) { + return parentMapping.getPropertyRelation(); + } + + return propRelation; } - public Relation getPropertyRelation (String propname) { - if (propname == null) - return getPropertyRelation (); - // first try finding an exact match for the property name - Relation rel = getExactPropertyRelation (propname); - // if not defined, return the generic property mapping - if (rel == null) - rel = getPropertyRelation (); - return rel; + /** + * + * + * @param propname ... + * + * @return ... + */ + public Relation getPropertyRelation(String propname) { + if (propname == null) { + return getPropertyRelation(); + } + + // first try finding an exact match for the property name + Relation rel = getExactPropertyRelation(propname); + + // if not defined, return the generic property mapping + if (rel == null) { + rel = getPropertyRelation(); + } + + return rel; } - public Relation getExactPropertyRelation (String propname) { - if (propname == null) - return null; - Relation rel = (Relation) prop2db.get (propname.toLowerCase()); - if (rel == null && parentMapping != null) - rel = parentMapping.getExactPropertyRelation (propname); - return rel; + /** + * + * + * @param propname ... + * + * @return ... + */ + public Relation getExactPropertyRelation(String propname) { + if (propname == null) { + return null; + } + + Relation rel = (Relation) prop2db.get(propname.toLowerCase()); + + if ((rel == null) && (parentMapping != null)) { + rel = parentMapping.getExactPropertyRelation(propname); + } + + return rel; } - public String getSubnodeGroupby () { - if (subRelation == null && parentMapping != null) - return parentMapping.getSubnodeGroupby (); - return subRelation == null ? null : subRelation.groupby; + /** + * + * + * @return ... + */ + public String getSubnodeGroupby() { + if ((subRelation == null) && (parentMapping != null)) { + return parentMapping.getSubnodeGroupby(); + } + + return (subRelation == null) ? null : subRelation.groupby; } - public String getIDgen () { - if (idgen == null && parentMapping != null) - return parentMapping.getIDgen (); - return idgen; + /** + * + * + * @return ... + */ + public String getIDgen() { + if ((idgen == null) && (parentMapping != null)) { + return parentMapping.getIDgen(); + } + + return idgen; } + /** + * + * + * @return ... + */ + public WrappedNodeManager getWrappedNodeManager() { + if (app == null) { + throw new RuntimeException("Can't get node manager from internal db mapping"); + } - public WrappedNodeManager getWrappedNodeManager () { - if (app == null) - throw new RuntimeException ("Can't get node manager from internal db mapping"); - return app.getWrappedNodeManager (); + return app.getWrappedNodeManager(); } /** @@ -573,186 +756,305 @@ public final class DbMapping implements Updatable { * mappings would be stored in the embedded db instead of an error being thrown, which is * not what we want. */ - public boolean isRelational () { - if (dbSourceName != null) - return true; - if (parentMapping != null) - return parentMapping.isRelational (); - return false; - } + public boolean isRelational() { + if (dbSourceName != null) { + return true; + } + if (parentMapping != null) { + return parentMapping.isRelational(); + } + + return false; + } /** * Return an array of DbColumns for the relational table mapped by this DbMapping. */ - public synchronized DbColumn[] getColumns() throws ClassNotFoundException, SQLException { - if (!isRelational ()) - throw new SQLException ("Can't get columns for non-relational data mapping "+this); - if (dbSource == null && parentMapping != null) - return parentMapping.getColumns (); - // Use local variable cols to avoid synchronization (schema may be nulled elsewhere) - if (columns == null) { - // we do two things here: set the SQL type on the Relation mappings - // and build a string of column names. - Connection con = getConnection (); - Statement stmt = con.createStatement (); - String t = getTableName(); - if (t == null) - throw new SQLException ("Table name is null in getColumns() for "+this); - ResultSet rs = stmt.executeQuery ( - new StringBuffer("SELECT * FROM ") - .append(t).append(" WHERE 1 = 0").toString()); - if (rs == null) - throw new SQLException ("Error retrieving columns for "+this); - ResultSetMetaData meta = rs.getMetaData (); - // ok, we have the meta data, now loop through mapping... - int ncols = meta.getColumnCount (); - columns = new DbColumn[ncols]; - for (int i=0; i lastRead; - if (!fileUpdated && defaultProps != null) - fileUpdated = defaultProps.lastModified () > lastRead; - if (con == null || con.isClosed () || fileUpdated) { - init (); - Class.forName (driver); - con = DriverManager.getConnection (url, user, password); - // If we wanted to use SQL transactions, we'd set autoCommit to - // false here and make commit/rollback invocations in Transactor methods; - // System.err.println ("Created new Connection to "+url); - tx.registerConnection (this, con); - ////////////////////////////////////////////// - /* DatabaseMetaData meta = con.getMetaData (); - ResultSet tables = meta.getCatalogs (); - while (tables.next()) - System.err.println ("********* TABLE: "+ tables.getObject (1)); - ResultSet types = meta.getTypeInfo (); - while (types.next()) - System.err.println ("******* TYPE: "+types.getObject(1) +" - "+types.getObject(2)+" - "+types.getObject(6)); - */ - } - return con; + /** + * + * + * @return ... + * + * @throws ClassNotFoundException ... + * @throws SQLException ... + */ + public Connection getConnection() throws ClassNotFoundException, SQLException { + Transactor tx = (Transactor) Thread.currentThread(); + Connection con = tx.getConnection(this); + boolean fileUpdated = props.lastModified() > lastRead; + + if (!fileUpdated && (defaultProps != null)) { + fileUpdated = defaultProps.lastModified() > lastRead; + } + + if ((con == null) || con.isClosed() || fileUpdated) { + init(); + Class.forName(driver); + con = DriverManager.getConnection(url, user, password); + + // If we wanted to use SQL transactions, we'd set autoCommit to + // false here and make commit/rollback invocations in Transactor methods; + // System.err.println ("Created new Connection to "+url); + tx.registerConnection(this, con); + } + + return con; } - private void init () throws ClassNotFoundException { - lastRead = defaultProps == null ? props.lastModified () : Math.max (props.lastModified (), defaultProps.lastModified ()); - url = props.getProperty (name+".url"); - driver = props.getProperty (name+".driver"); - Class.forName (driver); - user = props.getProperty (name+".user"); - password = props.getProperty (name+".password"); + private void init() throws ClassNotFoundException { + lastRead = (defaultProps == null) ? props.lastModified() + : Math.max(props.lastModified(), + defaultProps.lastModified()); + url = props.getProperty(name + ".url"); + driver = props.getProperty(name + ".driver"); + Class.forName(driver); + user = props.getProperty(name + ".user"); + password = props.getProperty(name + ".password"); } - public String getDriverName () { - return driver; + /** + * + * + * @return ... + */ + public String getDriverName() { + return driver; } - public String getName () { - return name; + /** + * + * + * @return ... + */ + public String getName() { + return name; } - public static void setDefaultProps (SystemProperties props) { - defaultProps = props; + /** + * + * + * @param props ... + */ + public static void setDefaultProps(SystemProperties props) { + defaultProps = props; } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/objectmodel/db/ExternalizableVector.java b/src/helma/objectmodel/db/ExternalizableVector.java index bcea489d..04e1bbe9 100644 --- a/src/helma/objectmodel/db/ExternalizableVector.java +++ b/src/helma/objectmodel/db/ExternalizableVector.java @@ -1,5 +1,18 @@ -// ExternalizableVector.java -// Copyright (c) Hannes Wallnöfer 1999-2000 +/* + * 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; @@ -7,33 +20,45 @@ import java.io.*; import java.util.ArrayList; /** - * A subclass of Vector that implements the Externalizable interface in order + * A subclass of Vector that implements the Externalizable interface in order * to be able to control how it is serialized and deserialized. */ - public class ExternalizableVector extends ArrayList implements Externalizable { - static final long serialVersionUID = 2316243615310540423L; - public synchronized void readExternal (ObjectInput in) throws IOException { - try { - int size = in.readInt (); - for (int i=0; i= 3) - prototype = (String) in.readObject (); - // if the input version is < 5, we have to do some conversion to make this object work - if (version < 5) { - if (rawParentID != null) - parentHandle = new NodeHandle (new DbKey (null, rawParentID)); - if (subnodes != null) { - for (int i=0; i 0) - newprop.setFloatValue (num.doubleValue ()); - else - newprop.setIntegerValue (num.longValue ()); - break; + created = lastmodified = System.currentTimeMillis(); - case Types.VARBINARY: - case Types.BINARY: - newprop.setStringValue (rs.getString(columns[i].getName())); - break; + for (int i = 0; i < columns.length; i++) { + Relation rel = columns[i].getRelation(); - 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, 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; + if ((rel == null) || + ((rel.reftype != Relation.PRIMITIVE) && + (rel.reftype != Relation.REFERENCE))) { + continue; + } - case Types.CHAR: - case Types.VARCHAR: - case Types.OTHER: - newprop.setStringValue (rs.getString(columns[i].getName())); - break; + Property newprop = new Property(rel.propName, this); - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: - newprop.setDateValue (rs.getTimestamp(columns[i].getName())); - break; + switch (columns[i].getType()) { + case Types.BIT: + newprop.setBooleanValue(rs.getBoolean(columns[i].getName())); - case Types.NULL: - newprop.setStringValue (null); - break; - // continue; + break; - default: - newprop.setStringValue (rs.getString(columns[i].getName())); - break; - } + case Types.TINYINT: + case Types.BIGINT: + case Types.SMALLINT: + case Types.INTEGER: + newprop.setIntegerValue(rs.getLong(columns[i].getName())); - if (rs.wasNull()) - newprop.setStringValue (null); + break; - if(propMap == null) - propMap = new Hashtable (); - propMap.put (rel.propName.toLowerCase(), newprop); + case Types.REAL: + case Types.FLOAT: + case Types.DOUBLE: + newprop.setFloatValue(rs.getDouble(columns[i].getName())); - // 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; - } + break; - // 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 (); - markAs (CLEAN); + 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(); + markAs(CLEAN); } + /** + * Read this object instance from a stream. This does some smart conversion to + * update from previous serialization formats. + */ + private void readObject(ObjectInputStream in) throws IOException { + try { + // as a general rule of thumb, if a string can be null use read/writeObject, + // if not it's save to use read/writeUTF. + // version indicates the serialization version + version = in.readShort(); + String rawParentID = null; - protected synchronized void checkWriteLock () { - // System.err.println ("registering writelock for "+this.getName ()+" ("+lock+") to "+Thread.currentThread ()); - if (state == TRANSIENT) - return; // no need to lock transient node - Transactor current = (Transactor) Thread.currentThread (); + id = in.readUTF(); + name = in.readUTF(); - if (!current.isActive ()) - throw new helma.framework.TimeoutException (); - if (state == INVALID) { - nmgr.logEvent ("Got Invalid Node: "+this); - Thread.dumpStack (); - throw new ConcurrencyException ("Node "+this+" was invalidated by another thread."); - } + if (version < 5) { + rawParentID = (String) in.readObject(); + } else { + parentHandle = (NodeHandle) in.readObject(); + } - if (lock != null && lock != current && lock.isAlive () && lock.isActive ()) { - nmgr.logEvent ("Concurrency conflict for "+this+", lock held by "+lock); - throw new ConcurrencyException ("Tried to modify "+this+" from two threads at the same time."); - } + created = in.readLong(); + lastmodified = in.readLong(); - current.visitNode (this); - lock = current; + if (version < 4) { + // read away content and contentType, which were dropped + in.readObject(); + in.readObject(); + } + + subnodes = (ExternalizableVector) in.readObject(); + links = (ExternalizableVector) in.readObject(); + + if (version < 6) { + // read away obsolete proplinks list + in.readObject(); + } + + propMap = (Hashtable) in.readObject(); + anonymous = in.readBoolean(); + + if (version == 2) { + prototype = in.readUTF(); + } else if (version >= 3) { + prototype = (String) in.readObject(); + } + + // if the input version is < 5, we have to do some conversion to make this object work + if (version < 5) { + if (rawParentID != null) { + parentHandle = new NodeHandle(new DbKey(null, rawParentID)); + } + + if (subnodes != null) { + for (int i = 0; i < subnodes.size(); i++) { + String s = (String) subnodes.get(i); + + subnodes.set(i, new NodeHandle(new DbKey(null, s))); + } + } + + if (links != null) { + for (int i = 0; i < links.size(); i++) { + String s = (String) links.get(i); + + links.set(i, new NodeHandle(new DbKey(null, s))); + } + } + } + } catch (ClassNotFoundException x) { + throw new IOException(x.toString()); + } } - protected synchronized void clearWriteLock () { - lock = null; + /** + * Write out this instance to a stream + */ + private void writeObject(ObjectOutputStream out) throws IOException { + out.writeShort(7); // serialization version + out.writeUTF(id); + out.writeUTF(name); + out.writeObject(parentHandle); + out.writeLong(created); + out.writeLong(lastmodified); + + DbMapping smap = (dbmap == null) ? null : dbmap.getSubnodeMapping(); + + if ((smap != null) && smap.isRelational()) { + out.writeObject(null); + } else { + out.writeObject(subnodes); + } + + out.writeObject(links); + out.writeObject(propMap); + out.writeBoolean(anonymous); + out.writeObject(prototype); } - - protected void markAs (int s) { - if (state == INVALID || state == VIRTUAL || state == TRANSIENT) - return; - state = s; - if (Thread.currentThread () instanceof Transactor) { - Transactor tx = (Transactor) Thread.currentThread (); - if (s == CLEAN) { - clearWriteLock (); - tx.dropNode (this); - } else { - tx.visitNode (this); - if (s == NEW) { - clearWriteLock (); - tx.visitCleanNode (this); - } - } - } + /** + * used by Xml deserialization + */ + public void setPropMap(Hashtable propMap) { + this.propMap = propMap; } - public int getState () { - return state; + /** + * used by Xml deserialization + */ + public void setSubnodes(List subnodes) { + this.subnodes = subnodes; } - public void setState (int s) { - this.state = s; + protected synchronized void checkWriteLock() { + // System.err.println ("registering writelock for "+this.getName ()+" ("+lock+") to "+Thread.currentThread ()); + if (state == TRANSIENT) { + return; // no need to lock transient node + } + + Transactor current = (Transactor) Thread.currentThread(); + + if (!current.isActive()) { + throw new helma.framework.TimeoutException(); + } + + if (state == INVALID) { + nmgr.logEvent("Got Invalid Node: " + this); + Thread.dumpStack(); + throw new ConcurrencyException("Node " + this + + " was invalidated by another thread."); + } + + if ((lock != null) && (lock != current) && lock.isAlive() && lock.isActive()) { + nmgr.logEvent("Concurrency conflict for " + this + ", lock held by " + lock); + throw new ConcurrencyException("Tried to modify " + this + + " from two threads at the same time."); + } + + current.visitNode(this); + lock = current; + } + + protected synchronized void clearWriteLock() { + lock = null; + } + + protected void markAs(int s) { + if ((state == INVALID) || (state == VIRTUAL) || (state == TRANSIENT)) { + return; + } + + state = s; + + if (Thread.currentThread() instanceof Transactor) { + Transactor tx = (Transactor) Thread.currentThread(); + + if (s == CLEAN) { + clearWriteLock(); + tx.dropNode(this); + } else { + tx.visitNode(this); + + if (s == NEW) { + clearWriteLock(); + tx.visitCleanNode(this); + } + } + } + } + + /** + * + * + * @return ... + */ + public int getState() { + return state; + } + + /** + * + * + * @param s ... + */ + public void setState(int s) { + this.state = s; } /** * Mark node as invalid so it is re-fetched from the database */ - public void invalidate () { - // This doesn't make sense for transient nodes - if (state == TRANSIENT || state == NEW) - return; - checkWriteLock (); - nmgr.evictNode (this); + public void invalidate() { + // This doesn't make sense for transient nodes + if ((state == TRANSIENT) || (state == NEW)) { + return; + } + + checkWriteLock(); + nmgr.evictNode(this); } /** * Check for a child mapping and evict the object specified by key from the cache */ - public void invalidateNode (String key) { - // This doesn't make sense for transient nodes - if (state == TRANSIENT || state == NEW) - return; - Relation rel = getDbMapping ().getSubnodeRelation (); - if (rel != null) { - if (rel.usesPrimaryKey()) { - nmgr.evictNodeByKey (new DbKey (getDbMapping().getSubnodeMapping(), key)); - } else { - nmgr.evictNodeByKey (new SyntheticKey (getKey(), key)); - } - } + public void invalidateNode(String key) { + // This doesn't make sense for transient nodes + if ((state == TRANSIENT) || (state == NEW)) { + return; + } + + Relation rel = getDbMapping().getSubnodeRelation(); + + if (rel != null) { + if (rel.usesPrimaryKey()) { + nmgr.evictNodeByKey(new DbKey(getDbMapping().getSubnodeMapping(), key)); + } else { + nmgr.evictNodeByKey(new SyntheticKey(getKey(), key)); + } + } } - - /** + /** * Get the ID of this Node. This is the primary database key and used as part of the * key for the internal node cache. */ - public String getID () { - // if we are transient, we generate an id on demand. It's possible that we'll never need - // it, but if we do it's important to keep the one we have. - if (state == TRANSIENT && id == null) { - id = TransientNode.generateID (); - } - return id; + public String getID() { + // if we are transient, we generate an id on demand. It's possible that we'll never need + // it, but if we do it's important to keep the one we have. + if ((state == TRANSIENT) && (id == null)) { + id = TransientNode.generateID(); + } + + return id; } /** * Returns true if this node is accessed by id from its aprent, false if it * is accessed by name */ - public boolean isAnonymous () { - return anonymous; + public boolean isAnonymous() { + return anonymous; } /** * Return this node' name, which may or may not have some meaning */ - public String getName () { - return name; + public String getName() { + return name; } - + /** * Get something to identify this node within a URL. This is the ID for anonymous nodes * and a property value for named properties. */ - public String getElementName () { - // if subnodes are also mounted as properties, try to get the "nice" prop value - // instead of the id by turning the anonymous flag off. - if (parentHandle != null && lastNameCheck < Math.max (dbmap.getLastTypeChange(), lastmodified)) { - try { - Node p = parentHandle.getNode (nmgr); - DbMapping parentmap = p.getDbMapping (); - Relation prel = parentmap.getPropertyRelation(); - if (prel != null && prel.hasAccessName()) { - String propname = dbmap.columnNameToProperty (prel.accessName); - String propvalue = getString (propname); - if (propvalue != null && propvalue.length() > 0) { - setName (propvalue); - anonymous = false; - // nameProp = localrel.propName; - } else { - anonymous = true; - } - } else if (!anonymous && p.contains (this) > -1) { - anonymous = true; - } - } catch (Exception ignore) { - // just fall back to default method - } - lastNameCheck = System.currentTimeMillis (); - } - return anonymous || name == null || name.length() == 0 ? id : name; + public String getElementName() { + // if subnodes are also mounted as properties, try to get the "nice" prop value + // instead of the id by turning the anonymous flag off. + if ((parentHandle != null) && + (lastNameCheck < Math.max(dbmap.getLastTypeChange(), lastmodified))) { + try { + Node p = parentHandle.getNode(nmgr); + DbMapping parentmap = p.getDbMapping(); + Relation prel = parentmap.getPropertyRelation(); + + if ((prel != null) && prel.hasAccessName()) { + String propname = dbmap.columnNameToProperty(prel.accessName); + String propvalue = getString(propname); + + if ((propvalue != null) && (propvalue.length() > 0)) { + setName(propvalue); + anonymous = false; + + // nameProp = localrel.propName; + } else { + anonymous = true; + } + } else if (!anonymous && (p.contains(this) > -1)) { + anonymous = true; + } + } catch (Exception ignore) { + // just fall back to default method + } + + lastNameCheck = System.currentTimeMillis(); + } + + return (anonymous || (name == null) || (name.length() == 0)) ? id : name; } - - public String getFullName () { - return getFullName (null); + /** + * + * + * @return ... + */ + public String getFullName() { + return getFullName(null); } - public String getFullName (INode root) { - String fullname = ""; - String divider = null; - StringBuffer b = new StringBuffer (); - INode p = this; - int loopWatch = 0; + /** + * + * + * @param root ... + * + * @return ... + */ + public String getFullName(INode root) { + String fullname = ""; + String divider = null; + StringBuffer b = new StringBuffer(); + INode p = this; + int loopWatch = 0; - while (p != null && p.getParent () != null && p != root) { - if (divider != null) - b.insert (0, divider); - else - divider = "/"; - b.insert (0, p.getElementName ()); - p = p.getParent (); + while ((p != null) && (p.getParent() != null) && (p != root)) { + if (divider != null) { + b.insert(0, divider); + } else { + divider = "/"; + } - loopWatch++; - if (loopWatch > 10) { - b.insert (0, "..."); - break; - } - } - return b.toString (); + b.insert(0, p.getElementName()); + p = p.getParent(); + + loopWatch++; + + if (loopWatch > 10) { + b.insert(0, "..."); + + break; + } + } + + return b.toString(); } + /** + * + * + * @return ... + */ + public String getPrototype() { + // if prototype is null, it's a vanilla HopObject. + if (prototype == null) { + return "hopobject"; + } - public String getPrototype () { - // if prototype is null, it's a vanilla HopObject. - if (prototype == null) - return "hopobject"; - return prototype; + return prototype; } - public void setPrototype (String proto) { - this.prototype = proto; + /** + * + * + * @param proto ... + */ + public void setPrototype(String proto) { + this.prototype = proto; } + /** + * + * + * @param dbmap ... + */ + public void setDbMapping(DbMapping dbmap) { + if (this.dbmap != dbmap) { + this.dbmap = dbmap; - public void setDbMapping (DbMapping dbmap) { - if (this.dbmap != dbmap) { - this.dbmap = dbmap; - // primaryKey = null; - } + // primaryKey = null; + } } - public DbMapping getDbMapping () { - return dbmap; + /** + * + * + * @return ... + */ + public DbMapping getDbMapping() { + return dbmap; } - public Key getKey () { - if (state == TRANSIENT) { - Thread.dumpStack (); - throw new RuntimeException ("getKey called on transient Node: "+this); - } - if (dbmap == null && prototype != null && nmgr != null) - dbmap = nmgr.getDbMapping (prototype); - if (primaryKey == null) - primaryKey = new DbKey (dbmap, id); - return primaryKey; + /** + * + * + * @return ... + */ + public Key getKey() { + if (state == TRANSIENT) { + Thread.dumpStack(); + throw new RuntimeException("getKey called on transient Node: " + this); + } + + if ((dbmap == null) && (prototype != null) && (nmgr != null)) { + dbmap = nmgr.getDbMapping(prototype); + } + + if (primaryKey == null) { + primaryKey = new DbKey(dbmap, id); + } + + return primaryKey; } - public NodeHandle getHandle () { - if (handle == null) - handle = new NodeHandle (this); - return handle; + /** + * + * + * @return ... + */ + public NodeHandle getHandle() { + if (handle == null) { + handle = new NodeHandle(this); + } + + return handle; } - public void setSubnodeRelation (String rel) { - if ((rel == null && this.subnodeRelation == null) - || (rel != null && rel.equalsIgnoreCase (this.subnodeRelation))) - return; - checkWriteLock (); - this.subnodeRelation = rel; - DbMapping smap = dbmap == null ? null : dbmap.getSubnodeMapping (); - if (smap != null && smap.isRelational ()) { - subnodes = null; - subnodeCount = -1; - } + /** + * + * + * @param rel ... + */ + public void setSubnodeRelation(String rel) { + if (((rel == null) && (this.subnodeRelation == null)) || + ((rel != null) && rel.equalsIgnoreCase(this.subnodeRelation))) { + return; + } + + checkWriteLock(); + this.subnodeRelation = rel; + + DbMapping smap = (dbmap == null) ? null : dbmap.getSubnodeMapping(); + + if ((smap != null) && smap.isRelational()) { + subnodes = null; + subnodeCount = -1; + } } - public String getSubnodeRelation () { - return subnodeRelation; + /** + * + * + * @return ... + */ + public String getSubnodeRelation() { + return subnodeRelation; } - public void setName (String name) { - // "/" is used as delimiter, so it's not a legal char - if (name.indexOf('/') > -1) - return; - // throw new RuntimeException ("The name of the node must not contain \"/\"."); - if (name == null || name.trim().length() == 0) - this.name = id; // use id as name - else - this.name = name; + /** + * + * + * @param name ... + */ + public void setName(String name) { + // "/" is used as delimiter, so it's not a legal char + if (name.indexOf('/') > -1) { + return; + } + + // throw new RuntimeException ("The name of the node must not contain \"/\"."); + if ((name == null) || (name.trim().length() == 0)) { + this.name = id; // use id as name + } else { + this.name = name; + } } /** * Register a node as parent of the present node. We can't refer to the node directly, so we use * the ID + DB map combo. */ - protected void setParent (Node parent) { - parentHandle = parent == null ? null : parent.getHandle (); + protected void setParent(Node parent) { + parentHandle = (parent == null) ? null : parent.getHandle(); } /** * Set the parent handle which can be used to get the actual parent node. */ - public void setParentHandle (NodeHandle parent) { - parentHandle = parent; + public void setParentHandle(NodeHandle parent) { + parentHandle = parent; } /** @@ -642,593 +811,799 @@ public final class Node implements INode, Serializable { * depending on the string argument. This is the version called from the scripting framework, * while the one argument version is called from within the objectmodel classes only. */ - public void setParent (Node parent, String propertyName) { - // we only do that for relational nodes. - if (!isRelational ()) - return; + public void setParent(Node parent, String propertyName) { + // we only do that for relational nodes. + if (!isRelational()) { + return; + } - NodeHandle oldParentHandle = parentHandle; - parentHandle = parent == null ? null : parent.getHandle (); - // mark parent as set, otherwise getParent will try to - // determine the parent again when called. - lastParentSet = System.currentTimeMillis (); - if (parentHandle == null || parentHandle.equals (oldParentHandle)) - // nothing changed, no need to find access property - return; + NodeHandle oldParentHandle = parentHandle; - if (parent != null && propertyName == null) { - // see if we can find out the propertyName by ourselfes by looking at the - // parent's property relation - String newname = null; - DbMapping parentmap = parent.getDbMapping (); - if (parentmap != null) { - // first try to retrieve name via generic property relation of parent - Relation prel = parentmap.getPropertyRelation (); - if (prel != null && prel.otherType == dbmap && prel.accessName != null) { - // reverse look up property used to access this via parent - Relation proprel = dbmap.columnNameToRelation (prel.accessName); - if (proprel != null && proprel.propName != null) - newname = getString (proprel.propName); - } - } + parentHandle = (parent == null) ? null : parent.getHandle(); - // did we find a new name for this - if (newname == null) { - this.anonymous = true; - } else { - this.anonymous = false; - this.name = newname; - } - } else { - this.anonymous = false; - this.name = propertyName; - } + // mark parent as set, otherwise getParent will try to + // determine the parent again when called. + lastParentSet = System.currentTimeMillis(); + + if ((parentHandle == null) || parentHandle.equals(oldParentHandle)) { + // nothing changed, no need to find access property + return; + } + + if ((parent != null) && (propertyName == null)) { + // see if we can find out the propertyName by ourselfes by looking at the + // parent's property relation + String newname = null; + DbMapping parentmap = parent.getDbMapping(); + + if (parentmap != null) { + // first try to retrieve name via generic property relation of parent + Relation prel = parentmap.getPropertyRelation(); + + if ((prel != null) && (prel.otherType == dbmap) && + (prel.accessName != null)) { + // reverse look up property used to access this via parent + Relation proprel = dbmap.columnNameToRelation(prel.accessName); + + if ((proprel != null) && (proprel.propName != null)) { + newname = getString(proprel.propName); + } + } + } + + // did we find a new name for this + if (newname == null) { + this.anonymous = true; + } else { + this.anonymous = false; + this.name = newname; + } + } else { + this.anonymous = false; + this.name = propertyName; + } } - /** * Get parent, retrieving it if necessary. */ - public INode getParent () { + public INode getParent() { + // check what's specified in the type.properties for this node. + ParentInfo[] parentInfo = null; - // check what's specified in the type.properties for this node. - ParentInfo[] parentInfo = null; - if (isRelational () && lastParentSet < Math.max (dbmap.getLastTypeChange(), lastmodified)) - parentInfo = dbmap.getParentInfo (); + if (isRelational() && + (lastParentSet < Math.max(dbmap.getLastTypeChange(), lastmodified))) { + parentInfo = dbmap.getParentInfo(); + } - // check if current parent candidate matches presciption, - // if not, try to get one that does. - if (parentInfo != null && state != TRANSIENT) { - for (int i=0; i -1) - // throw new RuntimeException ("\"/\" found in Node name."); + String n = node.getName(); - // only mark this node as modified if subnodes are not in relational db - // pointing to this node. - if (!ignoreSubnodeChange () && (state == CLEAN || state == DELETED)) - markAs (MODIFIED); - if (node.state == CLEAN || node.state == DELETED) - node.markAs (MODIFIED); + // if (n.indexOf('/') > -1) + // throw new RuntimeException ("\"/\" found in Node name."); + // only mark this node as modified if subnodes are not in relational db + // pointing to this node. + if (!ignoreSubnodeChange() && ((state == CLEAN) || (state == DELETED))) { + markAs(MODIFIED); + } - loadNodes (); + if ((node.state == CLEAN) || (node.state == DELETED)) { + node.markAs(MODIFIED); + } - // check if this node has a group-by subnode-relation - if (dbmap != null) { - Relation srel = dbmap.getSubnodeRelation (); - if (srel != null && srel.groupby != null) try { - Relation groupbyRel = srel.otherType.columnNameToRelation (srel.groupby); - String groupbyProp = (groupbyRel != null) ? - groupbyRel.propName : srel.groupby; - String groupbyValue = node.getString (groupbyProp); - INode groupbyNode = getNode (groupbyValue); - // if group-by node doesn't exist, we'll create it - if (groupbyNode == null) - groupbyNode = getGroupbySubnode (groupbyValue, true); - groupbyNode.addNode (node); + loadNodes(); - return node; - } catch (Exception x) { - System.err.println ("Error adding groupby: "+x); - // x.printStackTrace (); - return null; - } - } + // check if this node has a group-by subnode-relation + if (dbmap != null) { + Relation srel = dbmap.getSubnodeRelation(); + if ((srel != null) && (srel.groupby != null)) { + try { + Relation groupbyRel = srel.otherType.columnNameToRelation(srel.groupby); + String groupbyProp = (groupbyRel != null) ? groupbyRel.propName + : srel.groupby; + String groupbyValue = node.getString(groupbyProp); + INode groupbyNode = getNode(groupbyValue); - if (where < 0 || where > numberOfNodes ()) - where = numberOfNodes (); + // if group-by node doesn't exist, we'll create it + if (groupbyNode == null) { + groupbyNode = getGroupbySubnode(groupbyValue, true); + } - NodeHandle nhandle = node.getHandle (); - if (subnodes != null && subnodes.contains (nhandle)) { - // Node is already subnode of this - just move to new position - subnodes.remove (nhandle); - where = Math.min (where, numberOfNodes ()); - subnodes.add (where, nhandle); - } else { - if (subnodes == null) - subnodes = new ExternalizableVector (); - subnodes.add (where, nhandle); + groupbyNode.addNode(node); - // check if properties are subnodes (_properties.aresubnodes=true) - if (dbmap != null && node.dbmap != null) { - Relation prel = dbmap.getPropertyRelation(); - if (prel != null && prel.accessName != null) { - Relation localrel = node.dbmap.columnNameToRelation (prel.accessName); - // if no relation from db column to prop name is found, assume that both are equal - String propname = localrel == null ? prel.accessName : localrel.propName; - String prop = node.getString (propname); - if (prop != null && prop.length() > 0) { - INode old = getNode (prop); - if (old != null && old != node) { - unset (prop); - removeNode (old); - } - setNode (prop, node); - } - } - } + return node; + } catch (Exception x) { + System.err.println("Error adding groupby: " + x); - if (!"root".equalsIgnoreCase (node.getPrototype ())) { - // avoid calling getParent() because it would return bogus results for the not-anymore transient node - Node nparent = node.parentHandle == null ? null : node.parentHandle.getNode (nmgr); - // if the node doesn't have a parent yet, or it has one but it's transient while we are - // persistent, make this the nodes new parent. - if (nparent == null || (state != TRANSIENT && nparent.getState () == TRANSIENT)) { - node.setParent (this); - node.anonymous = true; - } else if (nparent != null && (nparent != this || !node.anonymous)) { - // this makes the additional job of addLink, registering that we have a link to a node in our - // subnodes that actually sits somewhere else. This means that addLink and addNode - // are actually the same now. - node.registerLinkFrom (this); - } - } - } + // x.printStackTrace (); + return null; + } + } + } - lastmodified = System.currentTimeMillis (); - lastSubnodeChange = lastmodified; - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, node)); - return node; + if ((where < 0) || (where > numberOfNodes())) { + where = numberOfNodes(); + } + + NodeHandle nhandle = node.getHandle(); + + if ((subnodes != null) && subnodes.contains(nhandle)) { + // Node is already subnode of this - just move to new position + subnodes.remove(nhandle); + where = Math.min(where, numberOfNodes()); + subnodes.add(where, nhandle); + } else { + if (subnodes == null) { + subnodes = new ExternalizableVector(); + } + + subnodes.add(where, nhandle); + + // check if properties are subnodes (_properties.aresubnodes=true) + if ((dbmap != null) && (node.dbmap != null)) { + Relation prel = dbmap.getPropertyRelation(); + + if ((prel != null) && (prel.accessName != null)) { + Relation localrel = node.dbmap.columnNameToRelation(prel.accessName); + + // if no relation from db column to prop name is found, assume that both are equal + String propname = (localrel == null) ? prel.accessName + : localrel.propName; + String prop = node.getString(propname); + + if ((prop != null) && (prop.length() > 0)) { + INode old = getNode(prop); + + if ((old != null) && (old != node)) { + unset(prop); + removeNode(old); + } + + setNode(prop, node); + } + } + } + + if (!"root".equalsIgnoreCase(node.getPrototype())) { + // avoid calling getParent() because it would return bogus results for the not-anymore transient node + Node nparent = (node.parentHandle == null) ? null + : node.parentHandle.getNode(nmgr); + + // if the node doesn't have a parent yet, or it has one but it's transient while we are + // persistent, make this the nodes new parent. + if ((nparent == null) || + ((state != TRANSIENT) && (nparent.getState() == TRANSIENT))) { + node.setParent(this); + node.anonymous = true; + } else if ((nparent != null) && ((nparent != this) || !node.anonymous)) { + // this makes the additional job of addLink, registering that we have a link to a node in our + // subnodes that actually sits somewhere else. This means that addLink and addNode + // are actually the same now. + node.registerLinkFrom(this); + } + } + } + + lastmodified = System.currentTimeMillis(); + lastSubnodeChange = lastmodified; + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, node)); + return node; } - - public INode createNode () { - // create new node at end of subnode array - return createNode (null, numberOfNodes ()); + /** + * + * + * @return ... + */ + public INode createNode() { + // create new node at end of subnode array + return createNode(null, numberOfNodes()); } - public INode createNode (int where) { - return createNode (null, where); + /** + * + * + * @param where ... + * + * @return ... + */ + public INode createNode(int where) { + return createNode(null, where); } - public INode createNode (String nm) { - // parameter where is ignored if nm != null so we try to avoid calling numberOfNodes() - return createNode (nm, nm == null ? numberOfNodes () : 0); + /** + * + * + * @param nm ... + * + * @return ... + */ + public INode createNode(String nm) { + // parameter where is ignored if nm != null so we try to avoid calling numberOfNodes() + return createNode(nm, (nm == null) ? numberOfNodes() : 0); } - public INode createNode (String nm, int where) { - checkWriteLock (); - boolean anon = false; - if (nm == null || "".equals (nm.trim ())) - anon = true; - Node n = new Node (nm, null, nmgr); - if (anon) - addNode (n, where); - else - setNode (nm, n); - return n; - } + /** + * + * + * @param nm ... + * @param where ... + * + * @return ... + */ + public INode createNode(String nm, int where) { + checkWriteLock(); + boolean anon = false; + + if ((nm == null) || "".equals(nm.trim())) { + anon = true; + } + + Node n = new Node(nm, null, nmgr); + + if (anon) { + addNode(n, where); + } else { + setNode(nm, n); + } + + return n; + } /** * register a node that links to this node so we can notify it when we cease to exist. * this is only necessary if we are a non-relational node, since for relational nodes * the referring object will notice that we've gone at runtime. */ - protected void registerLinkFrom (Node from) { - if (isRelational ()) - return; - if (from.getState () == TRANSIENT) - return; - if (links == null) - links = new ExternalizableVector (); - Object fromHandle = from.getHandle (); - if (!links.contains (fromHandle)) - links.add (fromHandle); + protected void registerLinkFrom(Node from) { + if (isRelational()) { + return; + } + + if (from.getState() == TRANSIENT) { + return; + } + + if (links == null) { + links = new ExternalizableVector(); + } + + Object fromHandle = from.getHandle(); + + if (!links.contains(fromHandle)) { + links.add(fromHandle); + } } /** * This implements the getChild() method of the IPathElement interface */ - public IPathElement getChildElement (String name) { - if (dbmap != null) { - // if a dbmapping is provided, check what it tells us about - // getting this specific child element - Relation rel = dbmap.getExactPropertyRelation (name); - if (rel != null) - return (IPathElement) getNode (name); - rel = dbmap.getSubnodeRelation (); - if (rel != null && rel.groupby == null && rel.accessName != null) { - if (rel.otherType != null && rel.otherType.isRelational ()) - return (IPathElement) nmgr.getNode (this, name, rel); - else - return (IPathElement) getNode (name); - } - return (IPathElement) getSubnode (name); - } else { - // no dbmapping - just try child collection first, then named property. - IPathElement child = (IPathElement) getSubnode (name); - if (child == null) - child = (IPathElement) getNode (name); - return child; - } + public IPathElement getChildElement(String name) { + if (dbmap != null) { + // if a dbmapping is provided, check what it tells us about + // getting this specific child element + Relation rel = dbmap.getExactPropertyRelation(name); + + if (rel != null) { + return (IPathElement) getNode(name); + } + + rel = dbmap.getSubnodeRelation(); + + if ((rel != null) && (rel.groupby == null) && (rel.accessName != null)) { + if ((rel.otherType != null) && rel.otherType.isRelational()) { + return (IPathElement) nmgr.getNode(this, name, rel); + } else { + return (IPathElement) getNode(name); + } + } + + return (IPathElement) getSubnode(name); + } else { + // no dbmapping - just try child collection first, then named property. + IPathElement child = (IPathElement) getSubnode(name); + + if (child == null) { + child = (IPathElement) getNode(name); + } + + return child; + } } /** * This implements the getParentElement() method of the IPathElement interface */ - public IPathElement getParentElement () { - return getParent (); + public IPathElement getParentElement() { + return getParent(); } + /** + * + * + * @param subid ... + * + * @return ... + */ + public INode getSubnode(String subid) { + // System.err.println ("GETSUBNODE : "+this+" > "+subid); + if ("".equals(subid)) { + return this; + } - public INode getSubnode (String subid) { - // System.err.println ("GETSUBNODE : "+this+" > "+subid); - if ("".equals (subid)) { - return this; - } - - Node retval = null; + Node retval = null; - if (subid != null) { + if (subid != null) { + loadNodes(); - loadNodes (); - if (subnodes == null || subnodes.size() == 0) - return null; - - NodeHandle nhandle = null; - int l = subnodes.size (); - for (int i=0; i index) { - // check if there is a group-by relation - retval = ((NodeHandle) subnodes.get (index)).getNode (nmgr); - - if (retval != null && retval.parentHandle == null && !"root".equalsIgnoreCase (retval.getPrototype ())) { - retval.setParent (this); - retval.anonymous = true; - } - } - return retval; + if (dbmap != null) { + smap = dbmap.getSubnodeMapping(); + } + + Node retval = null; + + if (subnodes.size() > index) { + // check if there is a group-by relation + retval = ((NodeHandle) subnodes.get(index)).getNode(nmgr); + + if ((retval != null) && (retval.parentHandle == null) && + !"root".equalsIgnoreCase(retval.getPrototype())) { + retval.setParent(this); + retval.anonymous = true; + } + } + + return retval; } - public Node getGroupbySubnode (String sid, boolean create) { - loadNodes (); - if (subnodes == null) - subnodes = new ExternalizableVector (); + /** + * + * + * @param sid ... + * @param create ... + * + * @return ... + */ + public Node getGroupbySubnode(String sid, boolean create) { + loadNodes(); - NodeHandle ghandle = new NodeHandle (new SyntheticKey (getKey(), sid)); - if (subnodes.contains (ghandle) || create) try { - DbMapping groupbyMapping = dbmap.getGroupbyMapping (); - boolean relational = groupbyMapping.getSubnodeMapping ().isRelational (); + if (subnodes == null) { + subnodes = new ExternalizableVector(); + } - if (relational || create) { - Node node = relational ? new Node (this, sid, nmgr, null) : new Node ("groupby-"+sid, null, nmgr); - // set "groupname" property to value of groupby field - node.setString ("groupname", sid); + NodeHandle ghandle = new NodeHandle(new SyntheticKey(getKey(), sid)); - if (relational) { - node.setDbMapping (groupbyMapping); - } else { - setNode (sid, node); - subnodes.add (node.getHandle ()); - } - node.setPrototype (groupbyMapping.getTypeName ()); - nmgr.evictKey (node.getKey ()); - return node; - } - } catch (Exception noluck) { - nmgr.logEvent ("Error creating group-by node for "+sid+": "+noluck); - noluck.printStackTrace(); - } - return null; + if (subnodes.contains(ghandle) || create) { + try { + DbMapping groupbyMapping = dbmap.getGroupbyMapping(); + boolean relational = groupbyMapping.getSubnodeMapping().isRelational(); + + if (relational || create) { + Node node = relational ? new Node(this, sid, nmgr, null) + : new Node("groupby-" + sid, null, nmgr); + + // set "groupname" property to value of groupby field + node.setString("groupname", sid); + + if (relational) { + node.setDbMapping(groupbyMapping); + } else { + setNode(sid, node); + subnodes.add(node.getHandle()); + } + + node.setPrototype(groupbyMapping.getTypeName()); + nmgr.evictKey(node.getKey()); + + return node; + } + } catch (Exception noluck) { + nmgr.logEvent("Error creating group-by node for " + sid + ": " + noluck); + noluck.printStackTrace(); + } + } + + return null; } - public boolean remove () { - checkWriteLock (); - try { - if (!anonymous) - getParent ().unset (name); - else - getParent ().removeNode (this); - } catch (Exception x) { - return false; - } - return true; + /** + * + * + * @return ... + */ + public boolean remove() { + checkWriteLock(); + + try { + if (!anonymous) { + getParent().unset(name); + } else { + getParent().removeNode(this); + } + } catch (Exception x) { + return false; + } + + return true; } + /** + * + * + * @param node ... + */ + public void removeNode(INode node) { + // nmgr.logEvent ("removing: "+ node); + Node n = (Node) node; - public void removeNode (INode node) { - // nmgr.logEvent ("removing: "+ node); - Node n = (Node) node; - checkWriteLock (); - n.checkWriteLock (); - - // need to query parent before releaseNode is called, since this may change the parent - // to the next option described in the type.properties _parent info - INode parent = n.getParent (); + checkWriteLock(); + n.checkWriteLock(); - releaseNode (n); + // need to query parent before releaseNode is called, since this may change the parent + // to the next option described in the type.properties _parent info + INode parent = n.getParent(); - if (parent == this) { - n.deepRemoveNode (); - } else { - // removed just a link, not the main node. - if (n.links != null) { - n.links.remove (getHandle ()); - if (n.state == CLEAN) n.markAs (MODIFIED); - } - } + releaseNode(n); + + if (parent == this) { + n.deepRemoveNode(); + } else { + // removed just a link, not the main node. + if (n.links != null) { + n.links.remove(getHandle()); + + if (n.state == CLEAN) { + n.markAs(MODIFIED); + } + } + } } /** * "Locally" remove a subnode from the subnodes table. * The logical stuff necessary for keeping data consistent is done in removeNode(). */ - protected void releaseNode (Node node) { - if (subnodes != null) - subnodes.remove (node.getHandle ()); + protected void releaseNode(Node node) { + if (subnodes != null) { + subnodes.remove(node.getHandle()); + } - lastSubnodeChange = System.currentTimeMillis (); + lastSubnodeChange = System.currentTimeMillis(); - // check if the subnode is in relational db and has a link back to this - // which needs to be unset - if (dbmap != null) { - Relation srel = dbmap.getSubnodeRelation (); - } + // check if the subnode is in relational db and has a link back to this + // which needs to be unset + if (dbmap != null) { + Relation srel = dbmap.getSubnodeRelation(); + } - // check if subnodes are also accessed as properties. If so, also unset the property - if (dbmap != null && node.dbmap != null) { - Relation prel = dbmap.getPropertyRelation(); - if (prel != null && prel.accessName != null) { - Relation localrel = node.dbmap.columnNameToRelation (prel.accessName); - // if no relation from db column to prop name is found, assume that both are equal - String propname = localrel == null ? prel.accessName : localrel.propName; - String prop = node.getString (propname); - if (prop != null && getNode (prop) == node) - unset (prop); - } - } + // check if subnodes are also accessed as properties. If so, also unset the property + if ((dbmap != null) && (node.dbmap != null)) { + Relation prel = dbmap.getPropertyRelation(); - // If subnodes are relational no need to mark this node as modified - if (ignoreSubnodeChange ()) - return; - - // Server.throwNodeEvent (new NodeEvent (node, NodeEvent.NODE_REMOVED)); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_REMOVED, node)); - lastmodified = System.currentTimeMillis (); - // nmgr.logEvent ("released node "+node +" from "+this+" oldobj = "+what); - if (state == CLEAN) markAs (MODIFIED); + if ((prel != null) && (prel.accessName != null)) { + Relation localrel = node.dbmap.columnNameToRelation(prel.accessName); + + // if no relation from db column to prop name is found, assume that both are equal + String propname = (localrel == null) ? prel.accessName : localrel.propName; + String prop = node.getString(propname); + + if ((prop != null) && (getNode(prop) == node)) { + unset(prop); + } + } + } + + // If subnodes are relational no need to mark this node as modified + if (ignoreSubnodeChange()) { + return; + } + + // Server.throwNodeEvent (new NodeEvent (node, NodeEvent.NODE_REMOVED)); + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_REMOVED, node)); + lastmodified = System.currentTimeMillis(); + + // nmgr.logEvent ("released node "+node +" from "+this+" oldobj = "+what); + if (state == CLEAN) { + markAs(MODIFIED); + } } - /** - * Delete the node from the db. This mainly tries to notify all nodes referring to this that - * it's going away. For nodes from the embedded db it also does a cascading delete, since - * it can tell which nodes are actual children and which are just linked in. - */ - protected void deepRemoveNode () { + /** + * Delete the node from the db. This mainly tries to notify all nodes referring to this that + * it's going away. For nodes from the embedded db it also does a cascading delete, since + * it can tell which nodes are actual children and which are just linked in. + */ + protected void deepRemoveNode() { + // notify nodes that link to this node being deleted. + int l = (links == null) ? 0 : links.size(); - // notify nodes that link to this node being deleted. - int l = links == null ? 0 : links.size (); - for (int i = 0; i < l; i++) { - NodeHandle lhandle = (NodeHandle) links.get (i); - Node link = lhandle.getNode (nmgr); - if (link != null) - link.releaseNode (this); - } + for (int i = 0; i < l; i++) { + NodeHandle lhandle = (NodeHandle) links.get(i); + Node link = lhandle.getNode(nmgr); - // tell all nodes that are properties of n that they are no longer used as such - if (propMap != null) { - for (Enumeration e2 = propMap.elements (); e2.hasMoreElements (); ) { - Property p = (Property) e2.nextElement (); - if (p != null && p.getType() == Property.NODE) - p.unregisterNode (); - } - } + if (link != null) { + link.releaseNode(this); + } + } - // cascading delete of all subnodes. This is never done for relational subnodes, because - // the parent info is not 100% accurate for them. - if (subnodes != null) { - Vector v = new Vector (); - // remove modifies the Vector we are enumerating, so we are extra careful. - for (Enumeration e3 = getSubnodes (); e3.hasMoreElements(); ) { - v.add (e3.nextElement()); - } - int m = v.size (); - for (int i=0; i= lastSubnodeCount || subnodeCount < 0) { - // count nodes in db without fetching anything - subnodeCount = nmgr.countNodes (this, subRel); - lastSubnodeCount = System.currentTimeMillis (); - } - return subnodeCount; - } - } - loadNodes (); - return subnodes == null ? 0 : subnodes.size (); + public int numberOfNodes() { + // If the subnodes are loaded aggressively, we really just + // do a count statement, otherwise we just return the size of the id index. + // (after loading it, if it's coming from a relational data source). + DbMapping smap = (dbmap == null) ? null : dbmap.getSubnodeMapping(); + + if ((smap != null) && smap.isRelational()) { + // check if subnodes need to be rechecked + Relation subRel = dbmap.getSubnodeRelation(); + + // 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. + if (subRel.aggressiveLoading && + (((state != TRANSIENT) && (state != NEW)) || + (subnodeRelation != null))) { + // we don't want to load *all* nodes if we just want to count them + long lastChange = subRel.aggressiveCaching ? lastSubnodeChange + : smap.getLastDataChange(); + + // also reload if the type mapping has changed. + lastChange = Math.max(lastChange, dbmap.getLastTypeChange()); + + if ((lastChange < lastSubnodeFetch) && (subnodes != null)) { + // we can use the nodes vector to determine number of subnodes + subnodeCount = subnodes.size(); + lastSubnodeCount = System.currentTimeMillis(); + } else if ((lastChange >= lastSubnodeCount) || (subnodeCount < 0)) { + // count nodes in db without fetching anything + subnodeCount = nmgr.countNodes(this, subRel); + lastSubnodeCount = System.currentTimeMillis(); + } + + return subnodeCount; + } + } + + loadNodes(); + + return (subnodes == null) ? 0 : subnodes.size(); } /** @@ -1236,566 +1611,860 @@ public final class Node implements INode, Serializable { * Depending on the subnode.loadmode specified in the type.properties, we'll load just the * ID index or the actual nodes. */ - protected void loadNodes () { - // Don't do this for transient nodes which don't have an explicit subnode relation set - if ((state == TRANSIENT || state == NEW) && subnodeRelation == null) - return; - - DbMapping smap = dbmap == null ? null : dbmap.getSubnodeMapping (); - if (smap != null && smap.isRelational ()) { - // check if subnodes need to be reloaded - Relation subRel = dbmap.getSubnodeRelation (); - synchronized (this) { - long lastChange = subRel.aggressiveCaching ? lastSubnodeChange : smap.getLastDataChange (); - // also reload if the type mapping has changed. - lastChange = Math.max (lastChange, dbmap.getLastTypeChange ()); - if (lastChange >= lastSubnodeFetch || subnodes == null) { - if (subRel.aggressiveLoading) - subnodes = nmgr.getNodes (this, dbmap.getSubnodeRelation()); - else - subnodes = nmgr.getNodeIDs (this, dbmap.getSubnodeRelation()); - lastSubnodeFetch = System.currentTimeMillis (); - } - } - } + protected void loadNodes() { + // Don't do this for transient nodes which don't have an explicit subnode relation set + if (((state == TRANSIENT) || (state == NEW)) && (subnodeRelation == null)) { + return; + } + + DbMapping smap = (dbmap == null) ? null : dbmap.getSubnodeMapping(); + + if ((smap != null) && smap.isRelational()) { + // check if subnodes need to be reloaded + Relation subRel = dbmap.getSubnodeRelation(); + + synchronized (this) { + long lastChange = subRel.aggressiveCaching ? lastSubnodeChange + : smap.getLastDataChange(); + + // also reload if the type mapping has changed. + lastChange = Math.max(lastChange, dbmap.getLastTypeChange()); + + if ((lastChange >= lastSubnodeFetch) || (subnodes == null)) { + if (subRel.aggressiveLoading) { + subnodes = nmgr.getNodes(this, dbmap.getSubnodeRelation()); + } else { + subnodes = nmgr.getNodeIDs(this, dbmap.getSubnodeRelation()); + } + + lastSubnodeFetch = System.currentTimeMillis(); + } + } + } } - public void prefetchChildren (int startIndex, int length) throws Exception { - if (length < 1) - return; - if (startIndex < 0) - return; - loadNodes (); - if (subnodes == null) - return; - if (startIndex >= subnodes.size()) - return; - int l = Math.min (subnodes.size()-startIndex, length); - if (l < 1) - return; - Key[] keys = new Key[l]; - for (int i=0; i= subnodes.size()) { + return; + } + + int l = Math.min(subnodes.size() - startIndex, length); + + if (l < 1) { + return; + } + + Key[] keys = new Key[l]; + + for (int i = 0; i < l; i++) { + keys[i] = ((NodeHandle) subnodes.get(i + startIndex)).getKey(); + } + + nmgr.nmgr.prefetchNodes(this, dbmap.getSubnodeRelation(), keys); } - public Enumeration getSubnodes () { - loadNodes (); - class Enum implements Enumeration { - int count = 0; - public boolean hasMoreElements () { - return count < numberOfNodes (); - } - public Object nextElement () { - return getSubnodeAt (count++); - } - } - return new Enum (); + /** + * + * + * @return ... + */ + public Enumeration getSubnodes() { + loadNodes(); + class Enum implements Enumeration { + int count = 0; + + public boolean hasMoreElements() { + return count < numberOfNodes(); + } + + public Object nextElement() { + return getSubnodeAt(count++); + } + } + + return new Enum(); } + /** + * + * + * @return ... + */ public List getSubnodeList() { - return subnodes; + return subnodes; } - private boolean ignoreSubnodeChange () { - // return true if a change in subnodes can be ignored because it is - // stored in the subnodes themselves. - Relation rel = dbmap == null ? null : dbmap.getSubnodeRelation(); - return (rel != null && rel.otherType != null && rel.otherType.isRelational()); + private boolean ignoreSubnodeChange() { + // return true if a change in subnodes can be ignored because it is + // stored in the subnodes themselves. + Relation rel = (dbmap == null) ? null : dbmap.getSubnodeRelation(); + + return ((rel != null) && (rel.otherType != null) && rel.otherType.isRelational()); } /** * Get all properties of this node. */ - public Enumeration properties () { + public Enumeration properties() { + if ((dbmap != null) && dbmap.isRelational()) { + // return the properties defined in type.properties, if there are any + return dbmap.getPropertyEnumeration(); + } - if (dbmap != null && dbmap.isRelational()) - // return the properties defined in type.properties, if there are any - return dbmap.getPropertyEnumeration(); + Relation prel = (dbmap == null) ? null : dbmap.getPropertyRelation(); - Relation prel = dbmap == null ? null : dbmap.getPropertyRelation (); - if (prel != null && prel.hasAccessName() - && prel.otherType != null && prel.otherType.isRelational ()) - // return names of objects from a relational db table - return nmgr.getPropertyNames (this, prel).elements (); - else if (propMap != null) - // return the actually explicitly stored properties - return propMap.keys (); + if ((prel != null) && prel.hasAccessName() && (prel.otherType != null) && + prel.otherType.isRelational()) { + // return names of objects from a relational db table + return nmgr.getPropertyNames(this, prel).elements(); + } else if (propMap != null) { + // return the actually explicitly stored properties + return propMap.keys(); + } - // sorry, no properties for this Node - return new EmptyEnumeration (); + // sorry, no properties for this Node + return new EmptyEnumeration(); - // NOTE: we don't enumerate node properties here - // return propMap == null ? new Vector ().elements () : propMap.elements (); + // NOTE: we don't enumerate node properties here + // return propMap == null ? new Vector ().elements () : propMap.elements (); } - public Hashtable getPropMap() { - return propMap; + /** + * + * + * @return ... + */ + public Hashtable getPropMap() { + return propMap; } - public IProperty get (String propname) { - return getProperty (propname); + /** + * + * + * @param propname ... + * + * @return ... + */ + public IProperty get(String propname) { + return getProperty(propname); } - public String getParentInfo () { - return "anonymous:"+anonymous+",parentHandle"+parentHandle+",parent:"+getParent(); + /** + * + * + * @return ... + */ + public String getParentInfo() { + return "anonymous:" + anonymous + ",parentHandle" + parentHandle + ",parent:" + + getParent(); } - protected Property getProperty (String propname) { - // nmgr.logEvent ("GETTING PROPERTY: "+propname); - if (propname == null) - return null; - Property prop = propMap == null ? null : (Property) propMap.get (propname.toLowerCase ()); + protected Property getProperty(String propname) { + // nmgr.logEvent ("GETTING PROPERTY: "+propname); + if (propname == null) { + return null; + } - // See if this could be a relationally linked node which still doesn't know - // (i.e, still thinks it's just the key as a string) - DbMapping pmap = dbmap == null ? null : dbmap.getExactPropertyMapping (propname); - if (pmap != null && prop != null && prop.getType() != IProperty.NODE) { - // this is a relational node stored by id but we still think it's just a string. Fix it - prop.convertToNodeReference (pmap); - } + Property prop = (propMap == null) ? null + : (Property) propMap.get(propname.toLowerCase()); - // the property does not exist in our propmap - see if we should create it on the fly, - // either because it is mapped to an object from relational database or defined as - // collection aka virtual node - if (prop == null && dbmap != null) { - Relation propRel = dbmap.getPropertyRelation (propname); - // if no property relation is defined for this specific property name, - // use the generic property relation, if one is defined. - if (propRel == null) - propRel = dbmap.getPropertyRelation (); - // so if we have a property relation and it does in fact link to another object... - if (propRel != null && propRel.isCollection ()) { - // 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) - // 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. - if (state == TRANSIENT && propRel.virtual) { - INode node = new Node (propname, propRel.getPrototype (), nmgr); - node.setDbMapping (propRel.getVirtualMapping ()); - setNode (propname, node); - prop = (Property) propMap.get (propname); - } - // if this is from relational database only fetch if this node - // is itself persistent. - else if (state != TRANSIENT && propRel.createPropertyOnDemand ()) { - // this may be a relational node stored by property name - try { - Node pn = nmgr.getNode (this, propname, propRel); - if (pn != null) { - if (pn.parentHandle == null && !"root".equalsIgnoreCase (pn.getPrototype ())) { - pn.setParent (this); - pn.name = propname; - pn.anonymous = false; - } - prop = new Property (propname, this, pn); - } - } catch (RuntimeException nonode) { - // wasn't a node after all - } - } - } - } + // See if this could be a relationally linked node which still doesn't know + // (i.e, still thinks it's just the key as a string) + DbMapping pmap = (dbmap == null) ? null : dbmap.getExactPropertyMapping(propname); - return prop; + if ((pmap != null) && (prop != null) && (prop.getType() != IProperty.NODE)) { + // this is a relational node stored by id but we still think it's just a string. Fix it + prop.convertToNodeReference(pmap); + } + + // the property does not exist in our propmap - see if we should create it on the fly, + // either because it is mapped to an object from relational database or defined as + // collection aka virtual node + if ((prop == null) && (dbmap != null)) { + Relation propRel = dbmap.getPropertyRelation(propname); + + // if no property relation is defined for this specific property name, + // use the generic property relation, if one is defined. + if (propRel == null) { + propRel = dbmap.getPropertyRelation(); + } + + // so if we have a property relation and it does in fact link to another object... + if ((propRel != null) && propRel.isCollection()) { + // 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) + // 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. + if ((state == TRANSIENT) && propRel.virtual) { + INode node = new Node(propname, propRel.getPrototype(), nmgr); + + node.setDbMapping(propRel.getVirtualMapping()); + setNode(propname, node); + prop = (Property) propMap.get(propname); + } + // if this is from relational database only fetch if this node + // is itself persistent. + else if ((state != TRANSIENT) && propRel.createPropertyOnDemand()) { + // this may be a relational node stored by property name + try { + Node pn = nmgr.getNode(this, propname, propRel); + + if (pn != null) { + if ((pn.parentHandle == null) && + !"root".equalsIgnoreCase(pn.getPrototype())) { + pn.setParent(this); + pn.name = propname; + pn.anonymous = false; + } + + prop = new Property(propname, this, pn); + } + } catch (RuntimeException nonode) { + // wasn't a node after all + } + } + } + } + + return prop; } - public String getString (String propname) { - // propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getStringValue (); - } catch (Exception ignore) {} - return null; + /** + * + * + * @param propname ... + * + * @return ... + */ + public String getString(String propname) { + // propname = propname.toLowerCase (); + Property prop = getProperty(propname); + + try { + return prop.getStringValue(); + } catch (Exception ignore) { + } + + return null; } - public long getInteger (String propname) { - // propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getIntegerValue (); - } catch (Exception ignore) {} - return 0; + /** + * + * + * @param propname ... + * + * @return ... + */ + public long getInteger(String propname) { + // propname = propname.toLowerCase (); + Property prop = getProperty(propname); + + try { + return prop.getIntegerValue(); + } catch (Exception ignore) { + } + + return 0; } - public double getFloat (String propname) { - // propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getFloatValue (); - } catch (Exception ignore) {} - return 0.0; + /** + * + * + * @param propname ... + * + * @return ... + */ + public double getFloat(String propname) { + // propname = propname.toLowerCase (); + Property prop = getProperty(propname); + + try { + return prop.getFloatValue(); + } catch (Exception ignore) { + } + + return 0.0; } - public Date getDate (String propname) { - // propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getDateValue (); - } catch (Exception ignore) {} - return null; + /** + * + * + * @param propname ... + * + * @return ... + */ + public Date getDate(String propname) { + // propname = propname.toLowerCase (); + Property prop = getProperty(propname); + + try { + return prop.getDateValue(); + } catch (Exception ignore) { + } + + return null; } + /** + * + * + * @param propname ... + * + * @return ... + */ + public boolean getBoolean(String propname) { + // propname = propname.toLowerCase (); + Property prop = getProperty(propname); - public boolean getBoolean (String propname) { - // propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getBooleanValue (); - } catch (Exception ignore) {} - return false; + try { + return prop.getBooleanValue(); + } catch (Exception ignore) { + } + + return false; } - public INode getNode (String propname) { - // propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getNodeValue (); - } catch (Exception ignore) {} - return null; + /** + * + * + * @param propname ... + * + * @return ... + */ + public INode getNode(String propname) { + // propname = propname.toLowerCase (); + Property prop = getProperty(propname); + + try { + return prop.getNodeValue(); + } catch (Exception ignore) { + } + + return null; } - public Object getJavaObject (String propname) { - // propname = propname.toLowerCase (); - Property prop = getProperty (propname); - try { - return prop.getJavaObjectValue (); - } catch (Exception ignore) {} - return null; + /** + * + * + * @param propname ... + * + * @return ... + */ + public Object getJavaObject(String propname) { + // propname = propname.toLowerCase (); + Property prop = getProperty(propname); + + try { + return prop.getJavaObjectValue(); + } catch (Exception ignore) { + } + + return null; } - public void setString (String propname, String value) { - // nmgr.logEvent ("setting String prop"); - checkWriteLock (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setString(String propname, String value) { + // nmgr.logEvent ("setting String prop"); + checkWriteLock(); - if (propMap == null) - propMap = new Hashtable (); + if (propMap == null) { + propMap = new Hashtable(); + } - propname = propname.trim (); - String p2 = propname.toLowerCase (); + propname = propname.trim(); - Property prop = (Property) propMap.get (p2); - String oldvalue = null; + String p2 = propname.toLowerCase(); - if (prop != null) { - oldvalue = prop.getStringValue (); - // check if the value has changed - if (value != null && value.equals (oldvalue)) - return; - prop.setStringValue (value); - } else { - prop = new Property (propname, this); - prop.setStringValue (value); - propMap.put (p2, prop); - } + Property prop = (Property) propMap.get(p2); + String oldvalue = null; - // check if this may have an effect on the node's URL when using accessname - // but only do this if we already have a parent set, i.e. if we are already stored in the db - Node parent = parentHandle == null ? null : (Node) getParent (); + if (prop != null) { + oldvalue = prop.getStringValue(); - if (dbmap != null && parent != null && parent.getDbMapping() != null) { - // check if this node is already registered with the old name; if so, remove it. - // then set parent's property to this node for the new name value - DbMapping parentmap = parent.getDbMapping (); - Relation propRel = parentmap.getPropertyRelation (); - String dbcolumn = dbmap.propertyToColumnName (propname); + // check if the value has changed + if ((value != null) && value.equals(oldvalue)) { + return; + } - if (propRel != null && propRel.accessName != null && propRel.accessName.equals (dbcolumn)) { - INode n = parent.getNode (value); - if (n != null && n != this) { - parent.unset (value); - parent.removeNode (n); - } + prop.setStringValue(value); + } else { + prop = new Property(propname, this); + prop.setStringValue(value); + propMap.put(p2, prop); + } - if (oldvalue != null) { - n = parent.getNode (oldvalue); - if (n == this) { - parent.unset (oldvalue); - parent.addNode (this); - // let the node cache know this key's not for this node anymore. - nmgr.evictKey (new SyntheticKey (parent.getKey (), oldvalue)); - } - } - parent.setNode (value, this); - setName (value); - } - } + // check if this may have an effect on the node's URL when using accessname + // but only do this if we already have a parent set, i.e. if we are already stored in the db + Node parent = (parentHandle == null) ? null : (Node) getParent(); - // check if the property we're setting specifies the prototype of this object. - if (dbmap != null && dbmap.getPrototypeField () != null) { - String pn = dbmap.columnNameToProperty (dbmap.getPrototypeField ()); - if (propname.equals (pn)) { - DbMapping newmap = nmgr.getDbMapping (value); - if (newmap != null) { - // see if old and new prototypes have same storage - otherwise type change is ignored - String oldStorage = dbmap.getStorageTypeName (); - String newStorage = newmap.getStorageTypeName (); - if ((oldStorage == null && newStorage == null) || - (oldStorage != null && oldStorage.equals (newStorage))) { - dbmap.notifyDataChange (); - newmap.notifyDataChange (); - this.dbmap = newmap; - this.prototype = value; - } - } - } - } + if ((dbmap != null) && (parent != null) && (parent.getDbMapping() != null)) { + // check if this node is already registered with the old name; if so, remove it. + // then set parent's property to this node for the new name value + DbMapping parentmap = parent.getDbMapping(); + Relation propRel = parentmap.getPropertyRelation(); + String dbcolumn = dbmap.propertyToColumnName(propname); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); - if (state == CLEAN) markAs (MODIFIED); + if ((propRel != null) && (propRel.accessName != null) && + propRel.accessName.equals(dbcolumn)) { + INode n = parent.getNode(value); + if ((n != null) && (n != this)) { + parent.unset(value); + parent.removeNode(n); + } + + if (oldvalue != null) { + n = parent.getNode(oldvalue); + + if (n == this) { + parent.unset(oldvalue); + parent.addNode(this); + + // let the node cache know this key's not for this node anymore. + nmgr.evictKey(new SyntheticKey(parent.getKey(), oldvalue)); + } + } + + parent.setNode(value, this); + setName(value); + } + } + + // check if the property we're setting specifies the prototype of this object. + if ((dbmap != null) && (dbmap.getPrototypeField() != null)) { + String pn = dbmap.columnNameToProperty(dbmap.getPrototypeField()); + + if (propname.equals(pn)) { + DbMapping newmap = nmgr.getDbMapping(value); + + if (newmap != null) { + // see if old and new prototypes have same storage - otherwise type change is ignored + String oldStorage = dbmap.getStorageTypeName(); + String newStorage = newmap.getStorageTypeName(); + + if (((oldStorage == null) && (newStorage == null)) || + ((oldStorage != null) && oldStorage.equals(newStorage))) { + dbmap.notifyDataChange(); + newmap.notifyDataChange(); + this.dbmap = newmap; + this.prototype = value; + } + } + } + } + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + + if (state == CLEAN) { + markAs(MODIFIED); + } } - public void setInteger (String propname, long value) { - // nmgr.logEvent ("setting bool prop"); - checkWriteLock (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setInteger(String propname, long value) { + // nmgr.logEvent ("setting bool prop"); + checkWriteLock(); - if (propMap == null) - propMap = new Hashtable (); - propname = propname.trim (); - String p2 = propname.toLowerCase (); + if (propMap == null) { + propMap = new Hashtable(); + } - Property prop = (Property) propMap.get (p2); - if (prop != null) { - prop.setIntegerValue (value); - } else { - prop = new Property (propname, this); - prop.setIntegerValue (value); - propMap.put (p2, prop); - } + propname = propname.trim(); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); - if (state == CLEAN) markAs (MODIFIED); + String p2 = propname.toLowerCase(); + + Property prop = (Property) propMap.get(p2); + + if (prop != null) { + prop.setIntegerValue(value); + } else { + prop = new Property(propname, this); + prop.setIntegerValue(value); + propMap.put(p2, prop); + } + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + + if (state == CLEAN) { + markAs(MODIFIED); + } } - public void setFloat (String propname, double value) { - // nmgr.logEvent ("setting bool prop"); - checkWriteLock (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setFloat(String propname, double value) { + // nmgr.logEvent ("setting bool prop"); + checkWriteLock(); - if (propMap == null) - propMap = new Hashtable (); - propname = propname.trim (); - String p2 = propname.toLowerCase (); + if (propMap == null) { + propMap = new Hashtable(); + } - Property prop = (Property) propMap.get (p2); - if (prop != null) { - prop.setFloatValue (value); - } else { - prop = new Property (propname, this); - prop.setFloatValue (value); - propMap.put (p2, prop); - } + propname = propname.trim(); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); - if (state == CLEAN) markAs (MODIFIED); + String p2 = propname.toLowerCase(); + + Property prop = (Property) propMap.get(p2); + + if (prop != null) { + prop.setFloatValue(value); + } else { + prop = new Property(propname, this); + prop.setFloatValue(value); + propMap.put(p2, prop); + } + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + + if (state == CLEAN) { + markAs(MODIFIED); + } } - public void setBoolean (String propname, boolean value) { - // nmgr.logEvent ("setting bool prop"); - checkWriteLock (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setBoolean(String propname, boolean value) { + // nmgr.logEvent ("setting bool prop"); + checkWriteLock(); - if (propMap == null) - propMap = new Hashtable (); - propname = propname.trim (); - String p2 = propname.toLowerCase (); + if (propMap == null) { + propMap = new Hashtable(); + } - Property prop = (Property) propMap.get (p2); - if (prop != null) { - prop.setBooleanValue (value); - } else { - prop = new Property (propname, this); - prop.setBooleanValue (value); - propMap.put (p2, prop); - } + propname = propname.trim(); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); - if (state == CLEAN) markAs (MODIFIED); + String p2 = propname.toLowerCase(); + + Property prop = (Property) propMap.get(p2); + + if (prop != null) { + prop.setBooleanValue(value); + } else { + prop = new Property(propname, this); + prop.setBooleanValue(value); + propMap.put(p2, prop); + } + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + + if (state == CLEAN) { + markAs(MODIFIED); + } } + /** + * + * + * @param propname ... + * @param value ... + */ + public void setDate(String propname, Date value) { + // nmgr.logEvent ("setting date prop"); + checkWriteLock(); - public void setDate (String propname, Date value) { - // nmgr.logEvent ("setting date prop"); - checkWriteLock (); + if (propMap == null) { + propMap = new Hashtable(); + } - if (propMap == null) - propMap = new Hashtable (); - propname = propname.trim (); - String p2 = propname.toLowerCase (); + propname = propname.trim(); - Property prop = (Property) propMap.get (p2); - if (prop != null) { - prop.setDateValue (value); - } else { - prop = new Property (propname, this); - prop.setDateValue (value); - propMap.put (p2, prop); - } + String p2 = propname.toLowerCase(); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); - if (state == CLEAN) markAs (MODIFIED); + Property prop = (Property) propMap.get(p2); + + if (prop != null) { + prop.setDateValue(value); + } else { + prop = new Property(propname, this); + prop.setDateValue(value); + propMap.put(p2, prop); + } + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + + if (state == CLEAN) { + markAs(MODIFIED); + } } - public void setJavaObject (String propname, Object value) { - // nmgr.logEvent ("setting jobject prop"); - checkWriteLock (); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setJavaObject(String propname, Object value) { + // nmgr.logEvent ("setting jobject prop"); + checkWriteLock(); - if (propMap == null) - propMap = new Hashtable (); - propname = propname.trim (); - String p2 = propname.toLowerCase (); + if (propMap == null) { + propMap = new Hashtable(); + } - Property prop = (Property) propMap.get (p2); - if (prop != null) { - prop.setJavaObjectValue (value); - } else { - prop = new Property (propname, this); - prop.setJavaObjectValue (value); - propMap.put (p2, prop); - } + propname = propname.trim(); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); - if (state == CLEAN) markAs (MODIFIED); + String p2 = propname.toLowerCase(); + + Property prop = (Property) propMap.get(p2); + + if (prop != null) { + prop.setJavaObjectValue(value); + } else { + prop = new Property(propname, this); + prop.setJavaObjectValue(value); + propMap.put(p2, prop); + } + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + + if (state == CLEAN) { + markAs(MODIFIED); + } } - public void setNode (String propname, INode value) { - // nmgr.logEvent ("setting node prop"); + /** + * + * + * @param propname ... + * @param value ... + */ + public void setNode(String propname, INode value) { + // nmgr.logEvent ("setting node prop"); + // check if types match, otherwise throw exception + DbMapping nmap = (dbmap == null) ? null : dbmap.getPropertyMapping(propname); - // check if types match, otherwise throw exception - DbMapping nmap = dbmap == null ? null : dbmap.getPropertyMapping (propname); - if (nmap != null && nmap != value.getDbMapping()) { - if (value.getDbMapping () == null) - value.setDbMapping (nmap); - else if (!nmap.isStorageCompatible (value.getDbMapping ())) - throw new RuntimeException ("Can't set "+propname+" to object with prototype "+value.getPrototype()+", was expecting "+nmap.getTypeName()); - } + if ((nmap != null) && (nmap != value.getDbMapping())) { + if (value.getDbMapping() == null) { + value.setDbMapping(nmap); + } else if (!nmap.isStorageCompatible(value.getDbMapping())) { + throw new RuntimeException("Can't set " + propname + + " to object with prototype " + + value.getPrototype() + ", was expecting " + + nmap.getTypeName()); + } + } - if (state != TRANSIENT) - checkWriteLock (); + if (state != TRANSIENT) { + checkWriteLock(); + } - Node n = null; - if (value instanceof Node) - n = (Node) value; - else - throw new RuntimeException ("Can't add fixed-transient node to a persistent node"); + Node n = null; - // if the new node is marked as TRANSIENT and this node is not, mark new node as NEW - if (state != TRANSIENT && n.state == TRANSIENT) - n.makePersistentCapable (); + if (value instanceof Node) { + n = (Node) value; + } else { + throw new RuntimeException("Can't add fixed-transient node to a persistent node"); + } - if (state != TRANSIENT) - n.checkWriteLock (); + // if the new node is marked as TRANSIENT and this node is not, mark new node as NEW + if ((state != TRANSIENT) && (n.state == TRANSIENT)) { + n.makePersistentCapable(); + } - // check if the main identity of this node is as a named property - // or as an anonymous node in a collection - if (n.parentHandle == null && !"root".equalsIgnoreCase (n.getPrototype ())) { - n.setParent (this); - n.name = propname; - n.anonymous = false; - } + if (state != TRANSIENT) { + n.checkWriteLock(); + } - propname = propname.trim (); - String p2 = propname.toLowerCase (); + // check if the main identity of this node is as a named property + // or as an anonymous node in a collection + if ((n.parentHandle == null) && !"root".equalsIgnoreCase(n.getPrototype())) { + n.setParent(this); + n.name = propname; + n.anonymous = false; + } - Property prop = propMap == null ? null : (Property) propMap.get (p2); - if (prop != null) { - if (prop.getType() == IProperty.NODE && n.getHandle ().equals (prop.getNodeHandle())) { - // nothing to do, just clean up locks and return - if (state == CLEAN) clearWriteLock (); - if (n.state == CLEAN) n.clearWriteLock (); - return; - } - } else { - prop = new Property (propname, this); - } + propname = propname.trim(); - prop.setNodeValue (n); - Relation rel = dbmap == null ? null : dbmap.getPropertyRelation (propname); + String p2 = propname.toLowerCase(); - if (rel == null || rel.reftype == Relation.REFERENCE || rel.virtual || - rel.otherType == null || !rel.otherType.isRelational()) { - // the node must be stored as explicit property - if (propMap == null) - propMap = new Hashtable (); - propMap.put (p2, prop); - if (state == CLEAN) markAs (MODIFIED); - } - - /* if (rel != null && rel.reftype == Relation.REFERENCE && !rel.usesPrimaryKey ()) { - // if the relation for this property doesn't use the primary key of the value object, make a - // secondary key object with the proper db column - String kval = n.getString (rel.otherType.columnNameToProperty (rel.getRemoteField ()), false); - prop.nhandle = new NodeHandle (new DbKey (n.getDbMapping (), kval, rel.getRemoteField ())); - } */ - - // don't check node in transactor cache if node is transient - - // this is done anyway when the node becomes persistent. - if (n.state != TRANSIENT) { - // check node in with transactor cache - // String nID = n.getID(); - // DbMapping dbm = n.getDbMapping (); - Transactor tx = (Transactor) Thread.currentThread (); - // tx.visitCleanNode (new DbKey (dbm, nID), n); - // UPDATE: using n.getKey() instead of manually constructing key. HW 2002/09/13 - tx.visitCleanNode (n.getKey(), n); - // if the field is not the primary key of the property, also register it - if (rel != null && rel.accessName != null && state != TRANSIENT) { - Key secKey = new SyntheticKey (getKey (), propname); - nmgr.evictKey (secKey); - tx.visitCleanNode (secKey, n); - } - } + Property prop = (propMap == null) ? null : (Property) propMap.get(p2); - lastmodified = System.currentTimeMillis (); - if (n.state == DELETED) n.markAs (MODIFIED); + if (prop != null) { + if ((prop.getType() == IProperty.NODE) && + n.getHandle().equals(prop.getNodeHandle())) { + // nothing to do, just clean up locks and return + if (state == CLEAN) { + clearWriteLock(); + } + + if (n.state == CLEAN) { + n.clearWriteLock(); + } + + return; + } + } else { + prop = new Property(propname, this); + } + + prop.setNodeValue(n); + + Relation rel = (dbmap == null) ? null : dbmap.getPropertyRelation(propname); + + if ((rel == null) || (rel.reftype == Relation.REFERENCE) || rel.virtual || + (rel.otherType == null) || !rel.otherType.isRelational()) { + // the node must be stored as explicit property + if (propMap == null) { + propMap = new Hashtable(); + } + + propMap.put(p2, prop); + + if (state == CLEAN) { + markAs(MODIFIED); + } + } + + /* if (rel != null && rel.reftype == Relation.REFERENCE && !rel.usesPrimaryKey ()) { + // if the relation for this property doesn't use the primary key of the value object, make a + // secondary key object with the proper db column + String kval = n.getString (rel.otherType.columnNameToProperty (rel.getRemoteField ()), false); + prop.nhandle = new NodeHandle (new DbKey (n.getDbMapping (), kval, rel.getRemoteField ())); + } */ + + // don't check node in transactor cache if node is transient - + // this is done anyway when the node becomes persistent. + if (n.state != TRANSIENT) { + // check node in with transactor cache + // String nID = n.getID(); + // DbMapping dbm = n.getDbMapping (); + Transactor tx = (Transactor) Thread.currentThread(); + + // tx.visitCleanNode (new DbKey (dbm, nID), n); + // UPDATE: using n.getKey() instead of manually constructing key. HW 2002/09/13 + tx.visitCleanNode(n.getKey(), n); + + // if the field is not the primary key of the property, also register it + if ((rel != null) && (rel.accessName != null) && (state != TRANSIENT)) { + Key secKey = new SyntheticKey(getKey(), propname); + + nmgr.evictKey(secKey); + tx.visitCleanNode(secKey, n); + } + } + + lastmodified = System.currentTimeMillis(); + + if (n.state == DELETED) { + n.markAs(MODIFIED); + } } /** * Remove a property. Note that this works only for explicitly set properties, not for those * specified via property relation. */ - public void unset (String propname) { - if (propMap == null) - return; - try { - // if node is relational, leave a null property so that it is - // updated in the DB. Otherwise, remove the property. - Property p; - boolean relational = dbmap != null && dbmap.isRelational(); - if (relational) - p = (Property) propMap.get (propname.toLowerCase ()); - else - p = (Property) propMap.remove (propname.toLowerCase ()); - if (p != null) { - checkWriteLock (); - if (p.getType() == Property.NODE) - p.unregisterNode (); - if (relational) - p.setStringValue (null); - // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); - lastmodified = System.currentTimeMillis (); - if (state == CLEAN) - markAs (MODIFIED); - } - } catch (Exception ignore) {} + public void unset(String propname) { + if (propMap == null) { + return; + } + + try { + // if node is relational, leave a null property so that it is + // updated in the DB. Otherwise, remove the property. + Property p; + boolean relational = (dbmap != null) && dbmap.isRelational(); + + if (relational) { + p = (Property) propMap.get(propname.toLowerCase()); + } else { + p = (Property) propMap.remove(propname.toLowerCase()); + } + + if (p != null) { + checkWriteLock(); + + if (p.getType() == Property.NODE) { + p.unregisterNode(); + } + + if (relational) { + p.setStringValue(null); + } + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); + lastmodified = System.currentTimeMillis(); + + if (state == CLEAN) { + markAs(MODIFIED); + } + } + } catch (Exception ignore) { + } } - public long lastModified () { - return lastmodified; + /** + * + * + * @return ... + */ + public long lastModified() { + return lastmodified; } - public long created () { - return created; + /** + * + * + * @return ... + */ + public long created() { + return created; } - public String toString () { - return "HopObject " + name; + /** + * + * + * @return ... + */ + public String toString() { + return "HopObject " + name; } /** @@ -1803,81 +2472,96 @@ public final class Node implements INode, Serializable { * it actually is stored in a relational db, just that it would be, if the node was * persistent */ - public boolean isRelational () { - return dbmap != null && dbmap.isRelational (); + public boolean isRelational() { + return (dbmap != null) && dbmap.isRelational(); } - /** * Recursively turn node status from TRANSIENT to NEW so that the Transactor will * know it has to insert this node. */ - protected void makePersistentCapable () { - if (state == TRANSIENT) { - state = NEW; - // generate a real, persistent ID for this object - id = nmgr.generateID (dbmap); - getHandle ().becomePersistent (); - Transactor current = (Transactor) Thread.currentThread (); - current.visitNode (this); - current.visitCleanNode (this); - for (Enumeration e = getSubnodes (); e.hasMoreElements (); ) { - Node n = (Node) e.nextElement (); - if (n.state == TRANSIENT) - n.makePersistentCapable (); - } - for (Enumeration e = properties (); e.hasMoreElements (); ) { - IProperty next = get ((String) e.nextElement ()); - if (next != null && next.getType () == IProperty.NODE) { - Node n = (Node) next.getNodeValue (); - if (n != null && n.state == TRANSIENT) - n.makePersistentCapable (); - } - } - } - } + protected void makePersistentCapable() { + if (state == TRANSIENT) { + state = NEW; + // generate a real, persistent ID for this object + id = nmgr.generateID(dbmap); + getHandle().becomePersistent(); + + Transactor current = (Transactor) Thread.currentThread(); + + current.visitNode(this); + current.visitCleanNode(this); + + for (Enumeration e = getSubnodes(); e.hasMoreElements();) { + Node n = (Node) e.nextElement(); + + if (n.state == TRANSIENT) { + n.makePersistentCapable(); + } + } + + for (Enumeration e = properties(); e.hasMoreElements();) { + IProperty next = get((String) e.nextElement()); + + if ((next != null) && (next.getType() == IProperty.NODE)) { + Node n = (Node) next.getNodeValue(); + + if ((n != null) && (n.state == TRANSIENT)) { + n.makePersistentCapable(); + } + } + } + } + } /** * Get the cache node for this node. This can be * used to store transient cache data per node from Javascript. */ - public synchronized INode getCacheNode () { - if (cacheNode == null) - cacheNode = new TransientNode(); - return cacheNode; + public synchronized INode getCacheNode() { + if (cacheNode == null) { + cacheNode = new TransientNode(); + } + + return cacheNode; } /** * Reset the cache node for this node. */ - public synchronized void clearCacheNode () { - cacheNode = null; + public synchronized void clearCacheNode() { + cacheNode = null; } - /** * 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. */ - public INode getNonVirtualParent () { - INode node = this; - for (int i=0; i<5; i++) { - if (node == null) break; - if (node.getState() != Node.VIRTUAL) - return node; - node = node.getParent (); - } - return null; - } + public INode getNonVirtualParent() { + INode node = this; + for (int i = 0; i < 5; i++) { + if (node == null) { + break; + } + + if (node.getState() != Node.VIRTUAL) { + return node; + } + + node = node.getParent(); + } + + return null; + } /** * Instances of this class may be used to mark an entry in the object cache as null. * This method tells the caller whether this is the case. */ - public boolean isNullNode () { - return nmgr == null; + public boolean isNullNode() { + return nmgr == null; } /** @@ -1885,19 +2569,20 @@ public final class Node implements INode, Serializable { * changes, the node will automatically get a new ESNode wrapper, since they're cached in a hashtable. * You gotta love these hash code tricks ;-) */ - public int hashCode () { - if (prototype == null) - return super.hashCode (); - else - return super.hashCode () + prototype.hashCode (); + public int hashCode() { + if (prototype == null) { + return super.hashCode(); + } else { + return super.hashCode() + prototype.hashCode(); + } } - public void dump () { - System.err.println ("subnodes: "+subnodes); - System.err.println ("properties: "+propMap); - System.err.println ("links: "+links); + /** + * + */ + public void dump() { + System.err.println("subnodes: " + subnodes); + System.err.println("properties: " + propMap); + System.err.println("links: " + links); } - } - - diff --git a/src/helma/objectmodel/db/NodeHandle.java b/src/helma/objectmodel/db/NodeHandle.java index 297126f6..35610fa2 100644 --- a/src/helma/objectmodel/db/NodeHandle.java +++ b/src/helma/objectmodel/db/NodeHandle.java @@ -1,6 +1,19 @@ -// NodeHandle.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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 helma.objectmodel.*; @@ -16,8 +29,8 @@ import java.io.Serializable; * and reinstanciated since being set, NodeHandle will always return an up-to-date * instance of its node. */ - public final class NodeHandle implements INodeState, Serializable { + static final long serialVersionUID = 3067763116576910931L; // direct reference to the node private Node node; @@ -25,20 +38,19 @@ public final class NodeHandle implements INodeState, Serializable { // the node's key private Key key; - static final long serialVersionUID = 3067763116576910931L; - /** * Builds a handle for a node */ - public NodeHandle (Node node) { - int state = node.getState (); - if (state == TRANSIENT) { - this.node = node; - key = null; - } else { - this.node = null; - key = node.getKey (); - } + public NodeHandle(Node node) { + int state = node.getState(); + + if (state == TRANSIENT) { + this.node = node; + key = null; + } else { + this.node = null; + key = node.getKey(); + } } /** @@ -46,73 +58,90 @@ public final class NodeHandle implements INodeState, Serializable { * the node is ususally not yet created. It will be fetched on demand when accessed by * application code. */ - public NodeHandle (Key key) { - this.node = null; - this.key = key; + public NodeHandle(Key key) { + this.node = null; + this.key = key; } /** * Get the node described by this node handle */ - public Node getNode (WrappedNodeManager nodemgr) { - if (node != null) - return node; - return nodemgr.getNode (key); + public Node getNode(WrappedNodeManager nodemgr) { + if (node != null) { + return node; + } + + return nodemgr.getNode(key); } /** - * Get the key for the node described by this handle. + * Get the key for the node described by this handle. * This may only be called on persistent Nodes. */ - public Key getKey () { - if (key == null) - throw new RuntimeException ("getKey called on transient Node"); - return key; + public Key getKey() { + if (key == null) { + throw new RuntimeException("getKey called on transient Node"); + } + + return key; } /** - * Get the ID for the node described by this handle. + * Get the ID for the node described by this handle. * This may only be called on persistent Nodes. */ - public String getID () { - if (key == null) - return node.getID (); - return key.getID (); + public String getID() { + if (key == null) { + return node.getID(); + } + + return key.getID(); } - private Object getObject () { - if (node != null) - return node; - else - return key; + private Object getObject() { + if (node != null) { + return node; + } else { + return key; + } } - public boolean equals (Object other) { - try { - return getObject ().equals (((NodeHandle) other).getObject ()); - } catch (Exception x) { - return false; - } + /** + * + * + * @param other ... + * + * @return ... + */ + public boolean equals(Object other) { + try { + return getObject().equals(((NodeHandle) other).getObject()); + } catch (Exception x) { + return false; + } } /** * This is to notify the handle that the underlying node is becoming * persistent and we have to refer to it via the key from now on. */ - protected void becomePersistent () { - if (node != null) { - key = node.getKey (); - node = null; - } - } - - - public String toString () { - if (node != null) - return "NodeHandle[transient:"+node+"]"; - else - return "NodeHandle["+key+"]"; + protected void becomePersistent() { + if (node != null) { + key = node.getKey(); + node = null; + } } + /** + * + * + * @return ... + */ + public String toString() { + if (node != null) { + return "NodeHandle[transient:" + node + "]"; + } else { + return "NodeHandle[" + key + "]"; + } + } } - diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index 87134589..d40c7d7a 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -1,903 +1,1054 @@ -// NodeManager.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * 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 helma.util.CacheMap; -import helma.objectmodel.*; import helma.framework.core.Application; -import java.sql.*; +import helma.objectmodel.*; +import helma.util.CacheMap; import java.io.*; +import java.sql.*; import java.util.*; /** - * The NodeManager is responsible for fetching Nodes from the internal or - * external data sources, caching them in a least-recently-used Hashtable, + * The NodeManager is responsible for fetching Nodes from the internal or + * external data sources, caching them in a least-recently-used Hashtable, * and writing changes back to the databases. */ - public final class NodeManager { protected Application app; - private CacheMap cache; - private Replicator replicator; - protected IDatabase db; - protected IDGenerator idgen; - - private long idBaseValue = 1l; - + private long idBaseValue = 1L; private boolean logSql; protected boolean logReplication; // a wrapper that catches some Exceptions while accessing this NM public final WrappedNodeManager safe; - /** - * Create a new NodeManager for Application app. An embedded database will be - * created in dbHome if one doesn't already exist. - */ - public NodeManager (Application app, String dbHome, Properties props) throws DatabaseException { - this.app = app; - int cacheSize = Integer.parseInt (props.getProperty ("cachesize", "1000")); - // Make actual cache size bigger, since we use it only up to the threshold - // cache = new CacheMap ((int) Math.ceil (cacheSize/0.75f), 0.75f); - cache = new CacheMap (cacheSize, 0.75f); - cache.setLogger (app.getLogger ("event")); - app.logEvent ("Setting up node cache ("+cacheSize+")"); + * Create a new NodeManager for Application app. An embedded database will be + * created in dbHome if one doesn't already exist. + */ + public NodeManager(Application app, String dbHome, Properties props) + throws DatabaseException { + this.app = app; - safe = new WrappedNodeManager (this); - // nullNode = new Node (); + int cacheSize = Integer.parseInt(props.getProperty("cachesize", "1000")); - logSql = "true".equalsIgnoreCase(props.getProperty ("logsql")); - logReplication = "true".equalsIgnoreCase(props.getProperty ("logReplication")); + // Make actual cache size bigger, since we use it only up to the threshold + // cache = new CacheMap ((int) Math.ceil (cacheSize/0.75f), 0.75f); + cache = new CacheMap(cacheSize, 0.75f); + cache.setLogger(app.getLogger("event")); + app.logEvent("Setting up node cache (" + cacheSize + ")"); + safe = new WrappedNodeManager(this); - String replicationUrl = props.getProperty ("replicationUrl"); - if (replicationUrl != null) { - if (logReplication) - app.logEvent ("Setting up replication listener at "+replicationUrl); - replicator = new Replicator (this); - replicator.addUrl (replicationUrl); - } else { - replicator = null; - } + // nullNode = new Node (); + logSql = "true".equalsIgnoreCase(props.getProperty("logsql")); + logReplication = "true".equalsIgnoreCase(props.getProperty("logReplication")); - // get the initial id generator value - String idb = props.getProperty ("idBaseValue"); - if (idb != null) try { - idBaseValue = Long.parseLong (idb); - idBaseValue = Math.max (1l, idBaseValue); // 0 and 1 are reserved for root nodes - } catch (NumberFormatException ignore) {} + String replicationUrl = props.getProperty("replicationUrl"); - db = new XmlDatabase (dbHome, null, this); - initDb (); + if (replicationUrl != null) { + if (logReplication) { + app.logEvent("Setting up replication listener at " + replicationUrl); + } + + replicator = new Replicator(this); + replicator.addUrl(replicationUrl); + } else { + replicator = null; + } + + // get the initial id generator value + String idb = props.getProperty("idBaseValue"); + + if (idb != null) { + try { + idBaseValue = Long.parseLong(idb); + idBaseValue = Math.max(1L, idBaseValue); // 0 and 1 are reserved for root nodes + } catch (NumberFormatException ignore) { + } + } + + db = new XmlDatabase(dbHome, null, this); + initDb(); } /** * app.properties file has been updated. Reread some settings. */ - public void updateProperties (Properties props) { - int cacheSize = Integer.parseInt (props.getProperty ("cachesize", "1000")); - cache.setCapacity (cacheSize); - logSql = "true".equalsIgnoreCase(props.getProperty ("logsql")); - logReplication = "true".equalsIgnoreCase(props.getProperty ("logReplication")); - } + public void updateProperties(Properties props) { + int cacheSize = Integer.parseInt(props.getProperty("cachesize", "1000")); - /** - * Method used to create the root node and id-generator, if they don't exist already. - */ - public void initDb () throws DatabaseException { - - ITransaction txn = null; - try { - txn = db.beginTransaction (); - - try { - idgen = db.getIDGenerator (txn); - if (idgen.getValue() < idBaseValue) { - idgen.setValue (idBaseValue); - db.saveIDGenerator (txn, idgen); - } - } catch (ObjectNotFoundException notfound) { - // will start with idBaseValue+1 - idgen = new IDGenerator (idBaseValue); - db.saveIDGenerator (txn, idgen); - } - - // check if we need to set the id generator to a base value - - Node node = null; - try { - node = (Node)db.getNode (txn, "0"); - node.nmgr = safe; - } catch (ObjectNotFoundException notfound) { - node = new Node ("root", "0", "root", safe); - node.setDbMapping (app.getDbMapping ("root")); - db.saveNode (txn, node.getID (), node); - registerNode (node); // register node with nodemanager cache - } - - try { - node = (Node)db.getNode (txn, "1"); - node.nmgr = safe; - } catch (ObjectNotFoundException notfound) { - node = new Node ("users", "1", null, safe); - node.setDbMapping (app.getDbMapping ("__userroot__")); - db.saveNode (txn, node.getID (), node); - registerNode (node); // register node with nodemanager cache - } - - db.commitTransaction (txn); - } catch (Exception x) { - System.err.println (">> "+x); - x.printStackTrace (); - try { - db.abortTransaction (txn); - } catch (Exception ignore) {} - throw (new DatabaseException ("Error initializing db")); - } - } - - - /** - * Shut down this node manager. This is called when the application using this - * node manager is stopped. - */ - public void shutdown () throws DatabaseException { - db.shutdown (); - if (cache != null) { - synchronized (cache) { - cache.clear (); - cache = null; - } - } + cache.setCapacity(cacheSize); + logSql = "true".equalsIgnoreCase(props.getProperty("logsql")); + logReplication = "true".equalsIgnoreCase(props.getProperty("logReplication")); } /** - * Delete a node from the database. - */ - public void deleteNode (Node node) throws Exception { - if (node != null) { - String id = node.getID (); - synchronized (this) { - Transactor tx = (Transactor) Thread.currentThread (); - node.setState (Node.INVALID); - deleteNode (db, tx.txn, node); - } - } - } + * Method used to create the root node and id-generator, if they don't exist already. + */ + public void initDb() throws DatabaseException { + ITransaction txn = null; + try { + txn = db.beginTransaction(); - /** - * Get a node by key. This is called from a node that already holds - * a reference to another node via a NodeHandle/Key. - */ - public Node getNode (Key key) throws Exception { + try { + idgen = db.getIDGenerator(txn); - Transactor tx = (Transactor) Thread.currentThread (); - // tx.timer.beginEvent ("getNode "+kstr); + if (idgen.getValue() < idBaseValue) { + idgen.setValue(idBaseValue); + db.saveIDGenerator(txn, idgen); + } + } catch (ObjectNotFoundException notfound) { + // will start with idBaseValue+1 + idgen = new IDGenerator(idBaseValue); + db.saveIDGenerator(txn, idgen); + } - // it would be a good idea to reuse key objects one day. - // Key key = new Key (dbmap, kstr); + // check if we need to set the id generator to a base value + Node node = null; - // See if Transactor has already come across this node - Node node = tx.getVisitedNode (key); + try { + node = (Node) db.getNode(txn, "0"); + node.nmgr = safe; + } catch (ObjectNotFoundException notfound) { + node = new Node("root", "0", "root", safe); + node.setDbMapping(app.getDbMapping("root")); + db.saveNode(txn, node.getID(), node); + registerNode(node); // register node with nodemanager cache + } - if (node != null && node.getState() != Node.INVALID) { - // tx.timer.endEvent ("getNode "+kstr); - return node; - } + try { + node = (Node) db.getNode(txn, "1"); + node.nmgr = safe; + } catch (ObjectNotFoundException notfound) { + node = new Node("users", "1", null, safe); + node.setDbMapping(app.getDbMapping("__userroot__")); + db.saveNode(txn, node.getID(), node); + registerNode(node); // register node with nodemanager cache + } - // try to get the node from the shared cache - node = (Node) cache.get (key); - if (node == null || node.getState() == Node.INVALID) { + db.commitTransaction(txn); + } catch (Exception x) { + System.err.println(">> " + x); + x.printStackTrace(); - // The requested node isn't in the shared cache. Synchronize with key to make sure only one - // version is fetched from the database. - if (key instanceof SyntheticKey) { - Node parent = getNode (key.getParentKey ()); - Relation rel = parent.dbmap.getPropertyRelation (key.getID()); - if (rel == null || rel.groupby != null) - node = parent.getGroupbySubnode (key.getID (), true); - else if (rel != null) - node = getNode (parent, key.getID (), rel); - else - node = null; - } else - node = getNodeByKey (tx.txn, (DbKey) key); - - if (node != null) { - synchronized (cache) { - Node oldnode = (Node) cache.put (node.getKey (), node); - if (oldnode != null && !oldnode.isNullNode() && oldnode.getState () != Node.INVALID) { - cache.put (node.getKey (), oldnode); - node = oldnode; - } - } // synchronized - } - } + try { + db.abortTransaction(txn); + } catch (Exception ignore) { + } - if (node != null) { - tx.visitCleanNode (key, node); - } - - // tx.timer.endEvent ("getNode "+kstr); - return node; - } - - - /** - * Get a node by relation, using the home node, the relation and a key to apply. - * In contrast to getNode (Key key), this is usually called when we don't yet know - * whether such a node exists. - */ - public Node getNode (Node home, String kstr, Relation rel) throws Exception { - - if (kstr == null) - return null; - - Transactor tx = (Transactor) Thread.currentThread (); - - Key key = null; - // check what kind of object we're looking for and make an apropriate key - if (rel.virtual || rel.groupby != null || !rel.usesPrimaryKey()) - // 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 - key = new SyntheticKey (home.getKey (), kstr); - else - // if a key for a node from within the DB - // FIXME: This should never apply, since for every relation-based loading Synthetic Keys are used. Right? - key = new DbKey (rel.otherType, kstr); - - // See if Transactor has already come across this node - Node node = tx.getVisitedNode (key); - - if (node != null && node.getState() != Node.INVALID) { - // we used to refresh the node in the main cache here to avoid the primary key entry being - // flushed from cache before the secondary one (risking duplicate nodes in cache) but - // we don't need to since we fetched the node from the threadlocal transactor cache and - // didn't refresh it in the main cache. - return node; - } - - // try to get the node from the shared cache - node = (Node) cache.get (key); - - // check if we can use the cached node without further checks. - // we need further checks for subnodes fetched by name if the subnodes were changed. - if (node != null && node.getState() != Node.INVALID) { - // check if node is null node (cached null) - if (node.isNullNode ()) { - if (node.created < rel.otherType.getLastDataChange () || - node.created < rel.ownType.getLastTypeChange()) - node = null; // cached null not valid anymore - } else if (!rel.virtual) { - // apply different consistency checks for groupby nodes and database nodes: - // for group nodes, check if they're contained - if (rel.groupby != null) { - if (home.contains (node) < 0) - node = null; - // for database nodes, check if constraints are fulfilled - } else if (!rel.usesPrimaryKey ()) { - if (!rel.checkConstraints (home, node)) - node = null; - } - } - } - - if (node == null || node.getState() == Node.INVALID) { - - // The requested node isn't in the shared cache. Synchronize with key to make sure only one - // version is fetched from the database. - node = getNodeByRelation (tx.txn, home, kstr, rel); - - if (node != null) { - - Key primKey = node.getKey (); - boolean keyIsPrimary = primKey.equals (key); - synchronized (cache) { - // check if node is already in cache with primary key - Node oldnode = (Node) cache.put (primKey, node); - // no need to check for oldnode != node because we fetched a new node from db - if (oldnode != null && !oldnode.isNullNode() && oldnode.getState () != Node.INVALID) { - // reset create time of old node, otherwise Relation.checkConstraints - // will reject it under certain circumstances. - oldnode.created = oldnode.lastmodified; - cache.put (primKey, oldnode); - if (!keyIsPrimary) { - cache.put (key, oldnode); - } - node = oldnode; - } else if (!keyIsPrimary) { - // cache node with secondary key - cache.put (key, node); - } - } // synchronized - } else { - // node fetched from db is null, cache result using nullNode - synchronized (cache) { - Node oldnode = (Node) cache.put (key, new Node ()); - // we ignore the case that onother thread has created the node in the meantime - return null; - } - } - } else if (node.isNullNode ()) { - // the nullNode caches a null value, i.e. an object that doesn't exist - return null; - } else { - // update primary key in cache to keep it from being flushed, see above - if (!rel.usesPrimaryKey ()) { - synchronized (cache) { - Node oldnode = (Node) cache.put (node.getKey (), node); - if (oldnode != node && oldnode != null && oldnode.getState () != Node.INVALID) { - cache.put (node.getKey (), oldnode); - cache.put (key, oldnode); - node = oldnode; - } - } - } - } - - if (node != null) { - tx.visitCleanNode (key, node); - } - - // tx.timer.endEvent ("getNode "+kstr); - return node; + throw (new DatabaseException("Error initializing db")); + } } /** - * Register a node in the node cache. - */ - public void registerNode (Node node) { - cache.put (node.getKey (), node); + * Shut down this node manager. This is called when the application using this + * node manager is stopped. + */ + public void shutdown() throws DatabaseException { + db.shutdown(); + + if (cache != null) { + synchronized (cache) { + cache.clear(); + cache = null; + } + } } /** - * Remove a node from the node cache. If at a later time it is accessed again, - * it will be refetched from the database. - */ - public void evictNode (Node node) { - node.setState (INode.INVALID); - cache.remove (node.getKey ()); + * Delete a node from the database. + */ + public void deleteNode(Node node) throws Exception { + if (node != null) { + String id = node.getID(); + + synchronized (this) { + Transactor tx = (Transactor) Thread.currentThread(); + + node.setState(Node.INVALID); + deleteNode(db, tx.txn, node); + } + } } /** - * Remove a node from the node cache. If at a later time it is accessed again, - * it will be refetched from the database. - */ - public void evictNodeByKey (Key key) { - Node n = (Node) cache.remove (key); - if (n != null) { - n.setState (INode.INVALID); - if (!(key instanceof DbKey)) - cache.remove (n.getKey ()); - } + * Get a node by key. This is called from a node that already holds + * a reference to another node via a NodeHandle/Key. + */ + public Node getNode(Key key) throws Exception { + Transactor tx = (Transactor) Thread.currentThread(); + + // tx.timer.beginEvent ("getNode "+kstr); + // it would be a good idea to reuse key objects one day. + // Key key = new Key (dbmap, kstr); + // See if Transactor has already come across this node + Node node = tx.getVisitedNode(key); + + if ((node != null) && (node.getState() != Node.INVALID)) { + // tx.timer.endEvent ("getNode "+kstr); + return node; + } + + // try to get the node from the shared cache + node = (Node) cache.get(key); + + if ((node == null) || (node.getState() == Node.INVALID)) { + // The requested node isn't in the shared cache. Synchronize with key to make sure only one + // version is fetched from the database. + if (key instanceof SyntheticKey) { + Node parent = getNode(key.getParentKey()); + Relation rel = parent.dbmap.getPropertyRelation(key.getID()); + + if ((rel == null) || (rel.groupby != null)) { + node = parent.getGroupbySubnode(key.getID(), true); + } else if (rel != null) { + node = getNode(parent, key.getID(), rel); + } else { + node = null; + } + } else { + node = getNodeByKey(tx.txn, (DbKey) key); + } + + if (node != null) { + synchronized (cache) { + Node oldnode = (Node) cache.put(node.getKey(), node); + + if ((oldnode != null) && !oldnode.isNullNode() && + (oldnode.getState() != Node.INVALID)) { + cache.put(node.getKey(), oldnode); + node = oldnode; + } + } + // synchronized + } + } + + if (node != null) { + tx.visitCleanNode(key, node); + } + + // tx.timer.endEvent ("getNode "+kstr); + return node; + } + + /** + * Get a node by relation, using the home node, the relation and a key to apply. + * In contrast to getNode (Key key), this is usually called when we don't yet know + * whether such a node exists. + */ + public Node getNode(Node home, String kstr, Relation rel) + throws Exception { + if (kstr == null) { + return null; + } + + Transactor tx = (Transactor) Thread.currentThread(); + + Key key = null; + + // check what kind of object we're looking for and make an apropriate key + if (rel.virtual || (rel.groupby != null) || !rel.usesPrimaryKey()) { + // 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 + key = new SyntheticKey(home.getKey(), kstr); + } else { + // if a key for a node from within the DB + // FIXME: This should never apply, since for every relation-based loading Synthetic Keys are used. Right? + key = new DbKey(rel.otherType, kstr); + } + + // See if Transactor has already come across this node + Node node = tx.getVisitedNode(key); + + if ((node != null) && (node.getState() != Node.INVALID)) { + // we used to refresh the node in the main cache here to avoid the primary key entry being + // flushed from cache before the secondary one (risking duplicate nodes in cache) but + // we don't need to since we fetched the node from the threadlocal transactor cache and + // didn't refresh it in the main cache. + return node; + } + + // try to get the node from the shared cache + node = (Node) cache.get(key); + + // check if we can use the cached node without further checks. + // we need further checks for subnodes fetched by name if the subnodes were changed. + if ((node != null) && (node.getState() != Node.INVALID)) { + // check if node is null node (cached null) + if (node.isNullNode()) { + if ((node.created < rel.otherType.getLastDataChange()) || + (node.created < rel.ownType.getLastTypeChange())) { + node = null; // cached null not valid anymore + } + } else if (!rel.virtual) { + // apply different consistency checks for groupby nodes and database nodes: + // for group nodes, check if they're contained + if (rel.groupby != null) { + if (home.contains(node) < 0) { + node = null; + } + + // for database nodes, check if constraints are fulfilled + } else if (!rel.usesPrimaryKey()) { + if (!rel.checkConstraints(home, node)) { + node = null; + } + } + } + } + + if ((node == null) || (node.getState() == Node.INVALID)) { + // The requested node isn't in the shared cache. Synchronize with key to make sure only one + // version is fetched from the database. + node = getNodeByRelation(tx.txn, home, kstr, rel); + + if (node != null) { + Key primKey = node.getKey(); + boolean keyIsPrimary = primKey.equals(key); + + synchronized (cache) { + // check if node is already in cache with primary key + Node oldnode = (Node) cache.put(primKey, node); + + // no need to check for oldnode != node because we fetched a new node from db + if ((oldnode != null) && !oldnode.isNullNode() && + (oldnode.getState() != Node.INVALID)) { + // reset create time of old node, otherwise Relation.checkConstraints + // will reject it under certain circumstances. + oldnode.created = oldnode.lastmodified; + cache.put(primKey, oldnode); + + if (!keyIsPrimary) { + cache.put(key, oldnode); + } + + node = oldnode; + } else if (!keyIsPrimary) { + // cache node with secondary key + cache.put(key, node); + } + } + // synchronized + } else { + // node fetched from db is null, cache result using nullNode + synchronized (cache) { + Node oldnode = (Node) cache.put(key, new Node()); + + // we ignore the case that onother thread has created the node in the meantime + return null; + } + } + } else if (node.isNullNode()) { + // the nullNode caches a null value, i.e. an object that doesn't exist + return null; + } else { + // update primary key in cache to keep it from being flushed, see above + if (!rel.usesPrimaryKey()) { + synchronized (cache) { + Node oldnode = (Node) cache.put(node.getKey(), node); + + if ((oldnode != node) && (oldnode != null) && + (oldnode.getState() != Node.INVALID)) { + cache.put(node.getKey(), oldnode); + cache.put(key, oldnode); + node = oldnode; + } + } + } + } + + if (node != null) { + tx.visitCleanNode(key, node); + } + + // tx.timer.endEvent ("getNode "+kstr); + return node; + } + + /** + * Register a node in the node cache. + */ + public void registerNode(Node node) { + cache.put(node.getKey(), node); + } + + /** + * Remove a node from the node cache. If at a later time it is accessed again, + * it will be refetched from the database. + */ + public void evictNode(Node node) { + node.setState(INode.INVALID); + cache.remove(node.getKey()); + } + + /** + * Remove a node from the node cache. If at a later time it is accessed again, + * it will be refetched from the database. + */ + public void evictNodeByKey(Key key) { + Node n = (Node) cache.remove(key); + + if (n != null) { + n.setState(INode.INVALID); + + if (!(key instanceof DbKey)) { + cache.remove(n.getKey()); + } + } } /** * Used when a key stops being valid for a node. The cached node itself * remains valid, if it is present in the cache by other keys. */ - public void evictKey (Key key) { - cache.remove (key); + public void evictKey(Key key) { + cache.remove(key); } - //////////////////////////////////////////////////////////////////////// // methods to do the actual db work //////////////////////////////////////////////////////////////////////// - /** - * Insert a new node in the embedded database or a relational database table, depending - * on its db mapping. - */ - public void insertNode (IDatabase db, ITransaction txn, Node node) throws Exception { + * Insert a new node in the embedded database or a relational database table, + * depending on its db mapping. + */ + public void insertNode(IDatabase db, ITransaction txn, Node node) + throws IOException, SQLException, ClassNotFoundException { + // Transactor tx = (Transactor) Thread.currentThread (); + // tx.timer.beginEvent ("insertNode "+node); + DbMapping dbm = node.getDbMapping(); - // Transactor tx = (Transactor) Thread.currentThread (); - // tx.timer.beginEvent ("insertNode "+node); + if ((dbm == null) || !dbm.isRelational()) { + db.saveNode(txn, node.getID(), node); + } else { + insertRelationalNode(node, dbm, dbm.getConnection()); + dbm.notifyDataChange(); + } - DbMapping dbm = node.getDbMapping (); - - if (dbm == null || !dbm.isRelational ()) { - db.saveNode (txn, node.getID (), node); - } else { - // app.logEvent ("inserting relational node: "+node.getID ()); - - DbColumn[] columns = dbm.getColumns (); - - StringBuffer b1 = dbm.getInsert (); - StringBuffer b2 = new StringBuffer (" ) VALUES ( ?"); - - String nameField = dbm.getNameField (); - String prototypeField = dbm.getPrototypeField (); - - for (int i=0; i 0) - stmt.setMaxRows (rel.maxSize); - ResultSet result = stmt.executeQuery (q); + if (rel.maxSize > 0) { + stmt.setMaxRows(rel.maxSize); + } - // problem: how do we derive a SyntheticKey from a not-yet-persistent Node? - Key k = rel.groupby != null ? home.getKey (): null; - while (result.next ()) { - String kstr = result.getString (1); - // jump over null values - this can happen especially when the selected - // column is a group-by column. - if (kstr == null) - continue; - // make the proper key for the object, either a generic DB key or a groupby key - Key key = rel.groupby == null ? - (Key) new DbKey (rel.otherType, kstr) : - (Key) new SyntheticKey (k, kstr); - retval.add (new NodeHandle (key)); - // if these are groupby nodes, evict nullNode keys - if (rel.groupby != null) { - Node n = (Node) cache.get (key); - if (n != null && n.isNullNode ()) - evictKey (key); - } - } + ResultSet result = stmt.executeQuery(q); - } finally { - // tx.timer.endEvent ("getNodeIDs "+home); - if (stmt != null) try { - stmt.close (); - } catch (Exception ignore) {} - } - return retval; - } + // problem: how do we derive a SyntheticKey from a not-yet-persistent Node? + Key k = (rel.groupby != null) ? home.getKey() : null; + + while (result.next()) { + String kstr = result.getString(1); + + // jump over null values - this can happen especially when the selected + // column is a group-by column. + if (kstr == null) { + continue; + } + + // make the proper key for the object, either a generic DB key or a groupby key + Key key = (rel.groupby == null) + ? (Key) new DbKey(rel.otherType, kstr) + : (Key) new SyntheticKey(k, kstr); + + retval.add(new NodeHandle(key)); + + // if these are groupby nodes, evict nullNode keys + if (rel.groupby != null) { + Node n = (Node) cache.get(key); + + if ((n != null) && n.isNullNode()) { + evictKey(key); + } + } + } + } finally { + // tx.timer.endEvent ("getNodeIDs "+home); + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ignore) { + } + } + } + + return retval; + } } /** @@ -905,528 +1056,636 @@ public final class NodeManager { * actually loades all nodes in one go, which is better for small node collections. * This method is used when xxx.loadmode=aggressive is specified. */ - public List getNodes (Node home, Relation rel) throws Exception { + public List getNodes(Node home, Relation rel) throws Exception { + // This does not apply for groupby nodes - use getNodeIDs instead + if (rel.groupby != null) { + return getNodeIDs(home, rel); + } - // This does not apply for groupby nodes - use getNodeIDs instead - if (rel.groupby != null) - return getNodeIDs (home, rel); + // Transactor tx = (Transactor) Thread.currentThread (); + // tx.timer.beginEvent ("getNodes "+home); + if ((rel == null) || (rel.otherType == null) || !rel.otherType.isRelational()) { + // this should never be called for embedded nodes + throw new RuntimeException("NodeMgr.countNodes called for non-relational node " + + home); + } else { + List retval = new ArrayList(); + DbMapping dbm = rel.otherType; - // Transactor tx = (Transactor) Thread.currentThread (); - // tx.timer.beginEvent ("getNodes "+home); + Connection con = dbm.getConnection(); + Statement stmt = con.createStatement(); + DbColumn[] columns = dbm.getColumns(); + StringBuffer q = dbm.getSelect(); - if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) { - // this should never be called for embedded nodes - throw new RuntimeException ("NodeMgr.countNodes called for non-relational node "+home); - } else { - List retval = new ArrayList (); - DbMapping dbm = rel.otherType; + try { + if (home.getSubnodeRelation() != null) { + q.append(home.getSubnodeRelation()); + } else { + // let relation object build the query + q.append(rel.buildQuery(home, home.getNonVirtualParent(), null, + " WHERE ", false)); - Connection con = dbm.getConnection (); - Statement stmt = con.createStatement (); - DbColumn[] columns = dbm.getColumns (); - StringBuffer q = dbm.getSelect (); - try { - if (home.getSubnodeRelation() != null) { - q.append (home.getSubnodeRelation()); - } else { - // let relation object build the query - q.append (rel.buildQuery (home, home.getNonVirtualParent (), null, " WHERE ", false)); - if (rel.getOrder () != null) - q.append (" order by "+rel.getOrder ()); - } + if (rel.getOrder() != null) { + q.append(" order by " + rel.getOrder()); + } + } - if (logSql) - app.logEvent ("### getNodes: "+q.toString()); + if (logSql) { + app.logEvent("### getNodes: " + q.toString()); + } - if (rel.maxSize > 0) - stmt.setMaxRows (rel.maxSize); + if (rel.maxSize > 0) { + stmt.setMaxRows(rel.maxSize); + } - ResultSet rs = stmt.executeQuery (q.toString ()); + ResultSet rs = stmt.executeQuery(q.toString()); - while (rs.next()) { - // create new Nodes. - Node node = new Node (rel.otherType, rs, columns, safe); - Key primKey = node.getKey (); - retval.add (new NodeHandle (primKey)); - // do we need to synchronize on primKey here? - synchronized (cache) { - Node oldnode = (Node) cache.put (primKey, node); - if (oldnode != null && oldnode.getState() != INode.INVALID) { - cache.put (primKey, oldnode); - } - } - } + while (rs.next()) { + // create new Nodes. + Node node = new Node(rel.otherType, rs, columns, safe); + Key primKey = node.getKey(); - } finally { - // tx.timer.endEvent ("getNodes "+home); - if (stmt != null) try { - stmt.close (); - } catch (Exception ignore) {} - } - return retval; - } + retval.add(new NodeHandle(primKey)); + + // do we need to synchronize on primKey here? + synchronized (cache) { + Node oldnode = (Node) cache.put(primKey, node); + + if ((oldnode != null) && (oldnode.getState() != INode.INVALID)) { + cache.put(primKey, oldnode); + } + } + } + } finally { + // tx.timer.endEvent ("getNodes "+home); + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ignore) { + } + } + } + + return retval; + } } /** * */ - public void prefetchNodes (Node home, Relation rel, Key[] keys) throws Exception { + public void prefetchNodes(Node home, Relation rel, Key[] keys) + throws Exception { + DbMapping dbm = rel.otherType; - DbMapping dbm = rel.otherType; - if (dbm == null || !dbm.isRelational ()) { - // this does nothing for objects in the embedded database - return; - } else { - int missing = cache.containsKeys (keys); - if (missing > 0) { - Connection con = dbm.getConnection (); - Statement stmt = con.createStatement (); - DbColumn[] columns = dbm.getColumns (); - StringBuffer q = dbm.getSelect (); - try { - String idfield = rel.groupby != null ? rel.groupby : dbm.getIDField (); - boolean needsQuotes = dbm.needsQuotes (idfield); - q.append ("WHERE "); - q.append (idfield); - q.append (" IN ("); - boolean first = true; - for (int i=0; i 0) { + Connection con = dbm.getConnection(); + Statement stmt = con.createStatement(); + DbColumn[] columns = dbm.getColumns(); + StringBuffer q = dbm.getSelect(); - ResultSet rs = stmt.executeQuery (q.toString());; + try { + String idfield = (rel.groupby != null) ? rel.groupby : dbm.getIDField(); + boolean needsQuotes = dbm.needsQuotes(idfield); - String groupbyProp = null; - HashMap groupbySubnodes = null; - if (rel.groupby != null) { - groupbyProp = dbm.columnNameToProperty (rel.groupby); - groupbySubnodes = new HashMap(); - } + q.append("WHERE "); + q.append(idfield); + q.append(" IN ("); - String accessProp = null; - if (rel.accessName != null && !rel.usesPrimaryKey ()) - accessProp = dbm.columnNameToProperty (rel.accessName); + boolean first = true; - while (rs.next ()) { - // create new Nodes. - Node node = new Node (dbm, rs, columns, safe); - Key primKey = node.getKey (); + for (int i = 0; i < keys.length; i++) { + if (keys[i] != null) { + if (!first) { + q.append(','); + } else { + first = false; + } - // for grouped nodes, collect subnode lists for the intermediary - // group nodes. - String groupName = null; - if (groupbyProp != null) { - groupName = node.getString (groupbyProp); - List sn = (List) groupbySubnodes.get (groupName); - if (sn == null) { - sn = new ExternalizableVector (); - groupbySubnodes.put (groupName, sn); - } - sn.add (new NodeHandle (primKey)); - } + if (needsQuotes) { + q.append("'"); + q.append(escape(keys[i].getID())); + q.append("'"); + } else { + q.append(keys[i].getID()); + } + } + } - // if relation doesn't use primary key as accessName, get accessName value - String accessName = null; - if (accessProp != null) { - accessName = node.getString (accessProp); - } + q.append(") "); - // 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); - } else if (accessName != null) { - // put the node into cache with its secondary key - if (groupName != null) - cache.put (new SyntheticKey (new SyntheticKey (home.getKey(), groupName), accessName), node); - else - cache.put (new SyntheticKey (home.getKey(), accessName), node); - } - } - } - // If these are grouped nodes, build the intermediary group nodes - // with the subnod lists we created - if (groupbyProp != null) { - for (Iterator i=groupbySubnodes.keySet().iterator(); i.hasNext(); ) { - String groupname = (String) i.next(); - if (groupname == null) continue; - Node groupnode = home.getGroupbySubnode (groupname, true); - cache.put (groupnode.getKey(), groupnode); - groupnode.setSubnodes ((List) groupbySubnodes.get(groupname)); - groupnode.lastSubnodeFetch = System.currentTimeMillis (); - } - } - } finally { - if (stmt != null) try { - stmt.close (); - } catch (Exception ignore) {} - } - } - } + if (rel.groupby != null) { + q.append(rel.renderConstraints(home, home.getNonVirtualParent())); + + if (rel.order != null) { + q.append(" ORDER BY "); + q.append(rel.order); + } + } + + if (logSql) { + app.logEvent("### prefetchNodes: " + q.toString()); + } + + ResultSet rs = stmt.executeQuery(q.toString()); + + ; + + String groupbyProp = null; + HashMap groupbySubnodes = null; + + if (rel.groupby != null) { + groupbyProp = dbm.columnNameToProperty(rel.groupby); + groupbySubnodes = new HashMap(); + } + + String accessProp = null; + + if ((rel.accessName != null) && !rel.usesPrimaryKey()) { + accessProp = dbm.columnNameToProperty(rel.accessName); + } + + while (rs.next()) { + // create new Nodes. + Node node = new Node(dbm, rs, columns, safe); + Key primKey = node.getKey(); + + // for grouped nodes, collect subnode lists for the intermediary + // group nodes. + String groupName = null; + + if (groupbyProp != null) { + groupName = node.getString(groupbyProp); + + List sn = (List) groupbySubnodes.get(groupName); + + if (sn == null) { + sn = new ExternalizableVector(); + groupbySubnodes.put(groupName, sn); + } + + sn.add(new NodeHandle(primKey)); + } + + // if relation doesn't use primary key as accessName, get accessName value + String accessName = null; + + if (accessProp != null) { + accessName = node.getString(accessProp); + } + + // 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); + } else if (accessName != null) { + // put the node into cache with its secondary key + if (groupName != null) { + cache.put(new SyntheticKey(new SyntheticKey(home.getKey(), + groupName), + accessName), node); + } else { + cache.put(new SyntheticKey(home.getKey(), accessName), + node); + } + } + } + } + + // If these are grouped nodes, build the intermediary group nodes + // with the subnod lists we created + if (groupbyProp != null) { + for (Iterator i = groupbySubnodes.keySet().iterator(); + i.hasNext();) { + String groupname = (String) i.next(); + + if (groupname == null) { + continue; + } + + Node groupnode = home.getGroupbySubnode(groupname, true); + + cache.put(groupnode.getKey(), groupnode); + groupnode.setSubnodes((List) groupbySubnodes.get(groupname)); + groupnode.lastSubnodeFetch = System.currentTimeMillis(); + } + } + } finally { + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ignore) { + } + } + } + } + } } - - /** * Count the nodes contained in the child collection of the home node * which is defined by Relation rel. */ - public int countNodes (Node home, Relation rel) throws Exception { + public int countNodes(Node home, Relation rel) throws Exception { + // Transactor tx = (Transactor) Thread.currentThread (); + // tx.timer.beginEvent ("countNodes "+home); + if ((rel == null) || (rel.otherType == null) || !rel.otherType.isRelational()) { + // this should never be called for embedded nodes + throw new RuntimeException("NodeMgr.countNodes called for non-relational node " + + home); + } else { + int retval = 0; + Connection con = rel.otherType.getConnection(); + String table = rel.otherType.getTableName(); - // Transactor tx = (Transactor) Thread.currentThread (); - // tx.timer.beginEvent ("countNodes "+home); + Statement stmt = null; - if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) { - // this should never be called for embedded nodes - throw new RuntimeException ("NodeMgr.countNodes called for non-relational node "+home); - } else { - int retval = 0; - Connection con = rel.otherType.getConnection (); - String table = rel.otherType.getTableName (); + try { + String q = null; - Statement stmt = null; - try { + if (home.getSubnodeRelation() != null) { + // use the manually set subnoderelation of the home node + q = new StringBuffer("SELECT count(*) FROM ").append(table).append(" ") + .append(home.getSubnodeRelation()) + .toString(); + } else { + // let relation object build the query + q = new StringBuffer("SELECT count(*) FROM ").append(table) + .append(rel.buildQuery(home, + home.getNonVirtualParent(), + null, + " WHERE ", + false)) + .toString(); + } - String q = null; - if (home.getSubnodeRelation() != null) { - // use the manually set subnoderelation of the home node - q = new StringBuffer("SELECT count(*) FROM ") - .append(table) - .append(" ") - .append(home.getSubnodeRelation()) - .toString(); - } else { - // let relation object build the query - q = new StringBuffer("SELECT count(*) FROM ") - .append(table) - .append(rel.buildQuery (home, home.getNonVirtualParent(), - null, " WHERE ", false)) - .toString(); - } + if (logSql) { + app.logEvent("### countNodes: " + q); + } - if (logSql) - app.logEvent ("### countNodes: "+q); + stmt = con.createStatement(); - stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery(q); - ResultSet rs = stmt.executeQuery (q); - if (!rs.next()) - retval = 0; - else - retval = rs.getInt (1); + if (!rs.next()) { + retval = 0; + } else { + retval = rs.getInt(1); + } + } finally { + // tx.timer.endEvent ("countNodes "+home); + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ignore) { + } + } + } - } finally { - // tx.timer.endEvent ("countNodes "+home); - if (stmt != null) try { - stmt.close (); - } catch (Exception ignore) {} - } - return rel.maxSize > 0 ? Math.min (rel.maxSize, retval) : retval; - } + return (rel.maxSize > 0) ? Math.min(rel.maxSize, retval) : retval; + } } /** * Similar to getNodeIDs, but returns a Vector that return's the nodes property names instead of IDs */ - public Vector getPropertyNames (Node home, Relation rel) throws Exception { + public Vector getPropertyNames(Node home, Relation rel) + throws Exception { + // Transactor tx = (Transactor) Thread.currentThread (); + // tx.timer.beginEvent ("getNodeIDs "+home); + if ((rel == null) || (rel.otherType == null) || !rel.otherType.isRelational()) { + // this should never be called for embedded nodes + throw new RuntimeException("NodeMgr.getPropertyNames called for non-relational node " + + home); + } else { + Vector retval = new Vector(); - // Transactor tx = (Transactor) Thread.currentThread (); - // tx.timer.beginEvent ("getNodeIDs "+home); + // if we do a groupby query (creating an intermediate layer of groupby nodes), + // retrieve the value of that field instead of the primary key + String namefield = rel.accessName; + Connection con = rel.otherType.getConnection(); + String table = rel.otherType.getTableName(); - if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) { - // this should never be called for embedded nodes - throw new RuntimeException ("NodeMgr.getPropertyNames called for non-relational node "+home); - } else { - Vector retval = new Vector (); - // if we do a groupby query (creating an intermediate layer of groupby nodes), - // retrieve the value of that field instead of the primary key - String namefield = rel.accessName; - Connection con = rel.otherType.getConnection (); - String table = rel.otherType.getTableName (); + Statement stmt = null; - Statement stmt = null; + try { + String q = new StringBuffer("SELECT ").append(namefield).append(" FROM ") + .append(table).append(" ORDER BY ") + .append(namefield).toString(); - try { - String q = new StringBuffer("SELECT ") - .append(namefield) - .append(" FROM ") - .append(table) - .append(" ORDER BY ") - .append(namefield) - .toString(); - stmt = con.createStatement (); + stmt = con.createStatement(); - if (logSql) - app.logEvent ("### getPropertyNames: "+q); + if (logSql) { + app.logEvent("### getPropertyNames: " + q); + } - ResultSet rs = stmt.executeQuery (q); - while (rs.next()) { - String n = rs.getString (1); - if (n != null) - retval.addElement (n); - } + ResultSet rs = stmt.executeQuery(q); - } finally { - // tx.timer.endEvent ("getNodeIDs "+home); - if (stmt != null) try { - stmt.close (); - } catch (Exception ignore) {} - } - return retval; - } + while (rs.next()) { + String n = rs.getString(1); + + if (n != null) { + retval.addElement(n); + } + } + } finally { + // tx.timer.endEvent ("getNodeIDs "+home); + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ignore) { + } + } + } + + return retval; + } } - /////////////////////////////////////////////////////////////////////////////////////// // private getNode methods /////////////////////////////////////////////////////////////////////////////////////// + private Node getNodeByKey(ITransaction txn, DbKey key) + throws Exception { + // Note: Key must be a DbKey, otherwise will not work for relational objects + Node node = null; + DbMapping dbm = app.getDbMapping(key.getStorageName()); + String kstr = key.getID(); - private Node getNodeByKey (ITransaction txn, DbKey key) throws Exception { - // Note: Key must be a DbKey, otherwise will not work for relational objects - Node node = null; - DbMapping dbm = app.getDbMapping (key.getStorageName ()); - String kstr = key.getID (); + if ((dbm == null) || !dbm.isRelational()) { + node = (Node) db.getNode(txn, kstr); + node.nmgr = safe; - if (dbm == null || !dbm.isRelational ()) { - node = (Node)db.getNode (txn, kstr); - node.nmgr = safe; - if (node != null && dbm != null) - node.setDbMapping (dbm); - } else { - String idfield =dbm.getIDField (); + if ((node != null) && (dbm != null)) { + node.setDbMapping(dbm); + } + } else { + String idfield = dbm.getIDField(); - Statement stmt = null; - try { - Connection con = dbm.getConnection (); - stmt = con.createStatement (); + Statement stmt = null; - DbColumn[] columns = dbm.getColumns (); - StringBuffer q = dbm.getSelect (); - q.append ("WHERE "); - q.append (idfield); - q.append (" = "); - if (dbm.needsQuotes (idfield)) { - q.append ("'"); - q.append (escape(kstr)); - q.append ("'"); - } else { - q.append (kstr); - } + try { + Connection con = dbm.getConnection(); - if (logSql) - app.logEvent ("### getNodeByKey: "+q.toString()); + stmt = con.createStatement(); - ResultSet rs = stmt.executeQuery (q.toString()); + DbColumn[] columns = dbm.getColumns(); + StringBuffer q = dbm.getSelect(); - if (!rs.next ()) - return null; - node = new Node (dbm, rs, columns, safe); - if (rs.next ()) - throw new RuntimeException ("More than one value returned by query."); + q.append("WHERE "); + q.append(idfield); + q.append(" = "); - } finally { - if (stmt != null) try { - stmt.close (); - } catch (Exception ignore) {} - } - } - return node; + if (dbm.needsQuotes(idfield)) { + q.append("'"); + q.append(escape(kstr)); + q.append("'"); + } else { + q.append(kstr); + } + + if (logSql) { + app.logEvent("### getNodeByKey: " + q.toString()); + } + + ResultSet rs = stmt.executeQuery(q.toString()); + + if (!rs.next()) { + return null; + } + + node = new Node(dbm, rs, columns, safe); + + if (rs.next()) { + throw new RuntimeException("More than one value returned by query."); + } + } finally { + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ignore) { + } + } + } + } + + return node; } - private Node getNodeByRelation (ITransaction txn, Node home, String kstr, Relation rel) throws Exception { - Node node = null; + private Node getNodeByRelation(ITransaction txn, Node home, String kstr, Relation rel) + throws Exception { + Node node = null; - if (rel != null && rel.virtual) { - if (rel.needsPersistence ()) - node = (Node) home.createNode (kstr); - else - node = new Node (home, kstr, safe, rel.prototype); - // set prototype and dbmapping on the newly created virtual/collection node - node.setPrototype (rel.prototype); - node.setDbMapping (rel.getVirtualMapping ()); + if ((rel != null) && rel.virtual) { + if (rel.needsPersistence()) { + node = (Node) home.createNode(kstr); + } else { + node = new Node(home, kstr, safe, rel.prototype); + } - } else if (rel != null && rel.groupby != null) { - node = home.getGroupbySubnode (kstr, false); - if (node == null && (rel.otherType == null || !rel.otherType.isRelational ())) { - node = (Node)db.getNode (txn, kstr); - node.nmgr = safe; - } - return node; + // set prototype and dbmapping on the newly created virtual/collection node + node.setPrototype(rel.prototype); + node.setDbMapping(rel.getVirtualMapping()); + } else if ((rel != null) && (rel.groupby != null)) { + node = home.getGroupbySubnode(kstr, false); - } else if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) { - node = (Node)db.getNode (txn, kstr); - node.nmgr = safe; - node.setDbMapping (rel.otherType); - return node; + if ((node == null) && + ((rel.otherType == null) || !rel.otherType.isRelational())) { + node = (Node) db.getNode(txn, kstr); + node.nmgr = safe; + } - } else { - Statement stmt = null; - try { - DbMapping dbm = rel.otherType; + return node; + } else if ((rel == null) || (rel.otherType == null) || + !rel.otherType.isRelational()) { + node = (Node) db.getNode(txn, kstr); + node.nmgr = safe; + node.setDbMapping(rel.otherType); - Connection con = dbm.getConnection (); - DbColumn[] columns = dbm.getColumns (); - StringBuffer q = dbm.getSelect (); - if (home.getSubnodeRelation () != null) { - // combine our key with the constraints in the manually set subnode relation - q.append ("WHERE "); - q.append (rel.accessName); - q.append (" = '"); - q.append (escape(kstr)); - q.append ("'"); - q.append (" AND "); - q.append (home.getSubnodeRelation().trim().substring(5)); - } else { - q.append (rel.buildQuery (home, home.getNonVirtualParent (), kstr, "WHERE ", false)); - } - if (logSql) - app.logEvent ("### getNodeByRelation: "+q.toString()); + return node; + } else { + Statement stmt = null; - stmt = con.createStatement (); - ResultSet rs = stmt.executeQuery (q.toString()); + try { + DbMapping dbm = rel.otherType; - if (!rs.next ()) - return null; - node = new Node (rel.otherType, rs, columns, safe); - if (rs.next ()) - throw new RuntimeException ("More than one value returned by query."); + Connection con = dbm.getConnection(); + DbColumn[] columns = dbm.getColumns(); + StringBuffer q = dbm.getSelect(); - // Check if node is already cached with primary Key. - if (!rel.usesPrimaryKey()) { - Key pk = node.getKey(); - Node existing = (Node) cache.get (pk); - if (existing != null && existing.getState() != Node.INVALID) { - node = existing; - } - } + if (home.getSubnodeRelation() != null) { + // combine our key with the constraints in the manually set subnode relation + q.append("WHERE "); + q.append(rel.accessName); + q.append(" = '"); + q.append(escape(kstr)); + q.append("'"); + q.append(" AND "); + q.append(home.getSubnodeRelation().trim().substring(5)); + } else { + q.append(rel.buildQuery(home, home.getNonVirtualParent(), kstr, + "WHERE ", false)); + } - } finally { - if (stmt != null) try { - stmt.close (); - } catch (Exception ignore) {} - } - } - return node; + if (logSql) { + app.logEvent("### getNodeByRelation: " + q.toString()); + } + + stmt = con.createStatement(); + + ResultSet rs = stmt.executeQuery(q.toString()); + + if (!rs.next()) { + return null; + } + + node = new Node(rel.otherType, rs, columns, safe); + + if (rs.next()) { + throw new RuntimeException("More than one value returned by query."); + } + + // Check if node is already cached with primary Key. + if (!rel.usesPrimaryKey()) { + Key pk = node.getKey(); + Node existing = (Node) cache.get(pk); + + if ((existing != null) && (existing.getState() != Node.INVALID)) { + node = existing; + } + } + } finally { + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ignore) { + } + } + } + } + + return node; } /** * Get a DbMapping for a given prototype name. This is just a proxy * method to the app's getDbMapping() method. */ - public DbMapping getDbMapping (String protoname) { - return app.getDbMapping (protoname); + public DbMapping getDbMapping(String protoname) { + return app.getDbMapping(protoname); } // a utility method to escape single quotes - private String escape (String str) { - if (str == null) + private String escape(String str) { + if (str == null) { return null; - if (str.indexOf ("'") < 0) - return str; - int l = str.length(); - StringBuffer sbuf = new StringBuffer (l + 10); - for (int i=0; i -1; - String d = named ? desc.substring (0, n) : desc; + named = n > -1; - int dot = d.indexOf ("."); - if (dot > -1) { - propname = d.substring (0, dot).trim(); - virtualname = d.substring (dot+1).trim(); - } else { - propname = d.trim(); - virtualname = null; - } - - isroot = "root".equals (propname); - // System.err.println ("created "+this); - } + String d = named ? desc.substring(0, n) : desc; - public String toString () { - return "ParentInfo["+propname+","+virtualname+","+named+"]"; + int dot = d.indexOf("."); + + if (dot > -1) { + propname = d.substring(0, dot).trim(); + virtualname = d.substring(dot + 1).trim(); + } else { + propname = d.trim(); + virtualname = null; + } + + isroot = "root".equals(propname); + + // System.err.println ("created "+this); } + /** + * + * + * @return ... + */ + public String toString() { + return "ParentInfo[" + propname + "," + virtualname + "," + named + "]"; + } } diff --git a/src/helma/objectmodel/db/Property.java b/src/helma/objectmodel/db/Property.java index beabe7fa..bd513008 100644 --- a/src/helma/objectmodel/db/Property.java +++ b/src/helma/objectmodel/db/Property.java @@ -1,342 +1,558 @@ -// Property.java -// Copyright (c) Hannes Wallnöfer 1997-2000 - +/* + * 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 helma.util.*; -import java.util.*; -import java.io.*; -import java.text.*; import helma.objectmodel.*; +import helma.util.*; +import java.io.*; import java.sql.Timestamp; +import java.text.*; +import java.util.*; /** * A property implementation for Nodes stored inside a database. Basically * the same as for transient nodes, with a few hooks added. */ public final class Property implements IProperty, Serializable, Cloneable { - - + static final long serialVersionUID = -1022221688349192379L; private String propname; private Node node; - private Object value; - private int type; - transient boolean dirty; - static final long serialVersionUID = -1022221688349192379L; - - private void readObject (ObjectInputStream in) throws IOException { - try { - propname = in.readUTF (); - node = (Node) in.readObject (); - type = in.readInt (); - switch (type) { - case STRING: - // try to convert from old format - if (node.version < 7) - value = in.readUTF (); - else - value = in.readObject (); - break; - case BOOLEAN: - value = in.readBoolean () ? Boolean.TRUE : Boolean.FALSE; - break; - case INTEGER: - value = new Long (in.readLong ()); - break; - case DATE: - value = new Date (in.readLong ()); - break; - case FLOAT: - value = new Double (in.readDouble ()); - break; - case NODE: - // try to convert from old format - if (node.version > 4) - value = (NodeHandle) in.readObject (); - else - value = new NodeHandle (new DbKey (null, in.readUTF ())); - break; - case JAVAOBJECT: - value = in.readObject (); - break; - } - } catch (ClassNotFoundException x) { - throw new IOException (x.toString ()); - } + /** + * Creates a new Property object. + * + * @param node ... + */ + public Property(Node node) { + this.node = node; + dirty = true; } - private void writeObject (ObjectOutputStream out) throws IOException { - out.writeUTF (propname); - out.writeObject (node); - out.writeInt (type); - switch (type) { - case STRING: - out.writeObject (value); - break; - case BOOLEAN: - out.writeBoolean (((Boolean) value).booleanValue()); - break; - case INTEGER: - out.writeLong (((Long) value).longValue()); - break; - case DATE: - out.writeLong (((Date) value).getTime()); - break; - case FLOAT: - out.writeDouble (((Double) value).doubleValue()); - break; - case NODE: - out.writeObject (value); - break; - case JAVAOBJECT: - if (value != null && !(value instanceof Serializable)) - out.writeObject (null); - else - out.writeObject (value); - break; - } + /** + * Creates a new Property object. + * + * @param propname ... + * @param node ... + */ + public Property(String propname, Node node) { + this.propname = propname; + this.node = node; + dirty = true; } - - public Property (Node node) { - this.node = node; - dirty = true; + /** + * Creates a new Property object. + * + * @param propname ... + * @param node ... + * @param valueNode ... + */ + public Property(String propname, Node node, Node valueNode) { + this(propname, node); + type = NODE; + value = (valueNode == null) ? null : valueNode.getHandle(); + dirty = true; } - public Property (String propname, Node node) { - this.propname = propname; - this.node = node; - dirty = true; + private void readObject(ObjectInputStream in) throws IOException { + try { + propname = in.readUTF(); + node = (Node) in.readObject(); + type = in.readInt(); + + switch (type) { + case STRING: + + // try to convert from old format + if (node.version < 7) { + value = in.readUTF(); + } else { + value = in.readObject(); + } + + break; + + case BOOLEAN: + value = in.readBoolean() ? Boolean.TRUE : Boolean.FALSE; + + break; + + case INTEGER: + value = new Long(in.readLong()); + + break; + + case DATE: + value = new Date(in.readLong()); + + break; + + case FLOAT: + value = new Double(in.readDouble()); + + break; + + case NODE: + + // try to convert from old format + if (node.version > 4) { + value = (NodeHandle) in.readObject(); + } else { + value = new NodeHandle(new DbKey(null, in.readUTF())); + } + + break; + + case JAVAOBJECT: + value = in.readObject(); + + break; + } + } catch (ClassNotFoundException x) { + throw new IOException(x.toString()); + } } - public Property (String propname, Node node, Node valueNode) { - this (propname, node); - type = NODE; - value = valueNode == null ? null : valueNode.getHandle (); - dirty = true; + private void writeObject(ObjectOutputStream out) throws IOException { + out.writeUTF(propname); + out.writeObject(node); + out.writeInt(type); + + switch (type) { + case STRING: + out.writeObject(value); + + break; + + case BOOLEAN: + out.writeBoolean(((Boolean) value).booleanValue()); + + break; + + case INTEGER: + out.writeLong(((Long) value).longValue()); + + break; + + case DATE: + out.writeLong(((Date) value).getTime()); + + break; + + case FLOAT: + out.writeDouble(((Double) value).doubleValue()); + + break; + + case NODE: + out.writeObject(value); + + break; + + case JAVAOBJECT: + + if ((value != null) && !(value instanceof Serializable)) { + out.writeObject(null); + } else { + out.writeObject(value); + } + + break; + } } - public String getName () { - return propname; + /** + * + * + * @return ... + */ + public String getName() { + return propname; } - public Object getValue () { - return value; + /** + * + * + * @return ... + */ + public Object getValue() { + return value; } - public int getType () { - return type; + /** + * + * + * @return ... + */ + public int getType() { + return type; } - public void setStringValue (String str) { - if (type == NODE) - unregisterNode (); - type = STRING; - value = str; - dirty = true; + /** + * + * + * @param str ... + */ + public void setStringValue(String str) { + if (type == NODE) { + unregisterNode(); + } + + type = STRING; + value = str; + dirty = true; } + /** + * + * + * @param l ... + */ + public void setIntegerValue(long l) { + if (type == NODE) { + unregisterNode(); + } - public void setIntegerValue (long l) { - if (type == NODE) - unregisterNode (); - type = INTEGER; - value = new Long(l); - dirty = true; + type = INTEGER; + value = new Long(l); + dirty = true; } - public void setFloatValue (double d) { - if (type == NODE) - unregisterNode (); - type = FLOAT; - value = new Double(d); - dirty = true; + /** + * + * + * @param d ... + */ + public void setFloatValue(double d) { + if (type == NODE) { + unregisterNode(); + } + + type = FLOAT; + value = new Double(d); + dirty = true; } - public void setDateValue (Date date) { - if (type == NODE) - unregisterNode (); - type = DATE; - value = date; - dirty = true; + /** + * + * + * @param date ... + */ + public void setDateValue(Date date) { + if (type == NODE) { + unregisterNode(); + } + + type = DATE; + value = date; + dirty = true; } - public void setBooleanValue (boolean bool) { - if (type == NODE) - unregisterNode (); - type = BOOLEAN; - value = bool ? Boolean.TRUE : Boolean.FALSE; - dirty = true; + /** + * + * + * @param bool ... + */ + public void setBooleanValue(boolean bool) { + if (type == NODE) { + unregisterNode(); + } + + type = BOOLEAN; + value = bool ? Boolean.TRUE : Boolean.FALSE; + dirty = true; } - public void setNodeValue (Node node) { - // value.checkWriteLock (); - if (type == NODE) - unregisterNode (); + /** + * + * + * @param node ... + */ + public void setNodeValue(Node node) { + // value.checkWriteLock (); + if (type == NODE) { + unregisterNode(); + } - // registerNode (value); - type = NODE; + // registerNode (value); + type = NODE; - value = node == null ? null : node.getHandle (); - dirty = true; + value = (node == null) ? null : node.getHandle(); + dirty = true; } - public void setNodeHandle (NodeHandle handle) { - if (type == NODE) - unregisterNode (); - // registerNode (value); - type = NODE; - value = handle; - dirty = true; + /** + * + * + * @param handle ... + */ + public void setNodeHandle(NodeHandle handle) { + if (type == NODE) { + unregisterNode(); + } + + // registerNode (value); + type = NODE; + value = handle; + dirty = true; } - public NodeHandle getNodeHandle () { - if (type == NODE) - return (NodeHandle) value; - return null; + /** + * + * + * @return ... + */ + public NodeHandle getNodeHandle() { + if (type == NODE) { + return (NodeHandle) value; + } + + return null; } - public void convertToNodeReference (DbMapping dbm) { - if (value != null && !(value instanceof NodeHandle)) - value = new NodeHandle (new DbKey (dbm, value.toString ())); - type = NODE; + /** + * + * + * @param dbm ... + */ + public void convertToNodeReference(DbMapping dbm) { + if ((value != null) && !(value instanceof NodeHandle)) { + value = new NodeHandle(new DbKey(dbm, value.toString())); + } + + type = NODE; } - public void setJavaObjectValue (Object obj) { - if (type == NODE) - unregisterNode (); - type = JAVAOBJECT; - value = obj; - } + /** + * + * + * @param obj ... + */ + public void setJavaObjectValue(Object obj) { + if (type == NODE) { + unregisterNode(); + } + type = JAVAOBJECT; + value = obj; + } /** * tell a the value node that it is no longer used as a property. * If this was the "main" property for the node, also remove all other references. */ - protected void unregisterNode () { - if (value == null || !(value instanceof NodeHandle)) - return; - NodeHandle nhandle = (NodeHandle) value; - Node nvalue = nhandle.getNode (node.nmgr); + protected void unregisterNode() { + if ((value == null) || !(value instanceof NodeHandle)) { + return; + } - DbMapping nvmap = null; - Relation nvrel = null; - if (node.dbmap != null) { - nvmap = node.dbmap.getPropertyMapping (propname); - nvrel = node.dbmap.getPropertyRelation (propname); - } + NodeHandle nhandle = (NodeHandle) value; + Node nvalue = nhandle.getNode(node.nmgr); - if (nvalue == null) - return; + DbMapping nvmap = null; + Relation nvrel = null; - nvalue.checkWriteLock (); - // check if the property node is also a subnode - // BUG: this doesn't work because properties for subnode/properties are never stored and therefore - // never reused. - if (nvrel != null && nvrel.hasAccessName()) { - node.removeNode (nvalue); - } - // only need to call unregisterPropLink if the value node is not stored in a relational db - // also, getParent is heuristical/implicit for relational nodes, so we don't do deepRemoveNode - // based on that for relational nodes. - if (nvmap == null || !nvmap.isRelational()) { - if (!nvalue.isAnonymous() && propname.equals (nvalue.getName()) && this.node == nvalue.getParent()) { - // this is the "main" property of a named node, so handle this as a cascading delete. - nvalue.deepRemoveNode (); - } - } + if (node.dbmap != null) { + nvmap = node.dbmap.getPropertyMapping(propname); + nvrel = node.dbmap.getPropertyRelation(propname); + } + + if (nvalue == null) { + return; + } + + nvalue.checkWriteLock(); + + // check if the property node is also a subnode + // BUG: this doesn't work because properties for subnode/properties are never stored and therefore + // never reused. + if ((nvrel != null) && nvrel.hasAccessName()) { + node.removeNode(nvalue); + } + + // only need to call unregisterPropLink if the value node is not stored in a relational db + // also, getParent is heuristical/implicit for relational nodes, so we don't do deepRemoveNode + // based on that for relational nodes. + if ((nvmap == null) || !nvmap.isRelational()) { + if (!nvalue.isAnonymous() && propname.equals(nvalue.getName()) && + (this.node == nvalue.getParent())) { + // this is the "main" property of a named node, so handle this as a cascading delete. + nvalue.deepRemoveNode(); + } + } } + /** + * + * + * @return ... + */ + public String getStringValue() { + if (value == null) { + return null; + } - public String getStringValue () { - if (value == null) - return null; - switch (type) { - case STRING: - case BOOLEAN: - case INTEGER: - case FLOAT: - case JAVAOBJECT: - return value.toString (); - case DATE: - SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm:ss"); - return format.format ((Date) value); - case NODE: - return ((NodeHandle) value).getID (); - } - return ""; + switch (type) { + case STRING: + case BOOLEAN: + case INTEGER: + case FLOAT: + case JAVAOBJECT: + return value.toString(); + + case DATE: + + SimpleDateFormat format = new SimpleDateFormat("dd.MM.yy hh:mm:ss"); + + return format.format((Date) value); + + case NODE: + return ((NodeHandle) value).getID(); + } + + return ""; } - public String toString () { - return getStringValue (); + /** + * + * + * @return ... + */ + public String toString() { + return getStringValue(); } - public long getIntegerValue () { - if (type == INTEGER) - return ((Long) value).longValue (); - if (type == FLOAT) - return ((Double) value).longValue (); - if (type == BOOLEAN) - return ((Boolean) value).booleanValue() ? 1 : 0; - try { - return Long.parseLong (getStringValue()); - } catch (Exception x) { - return 0; - } + /** + * + * + * @return ... + */ + public long getIntegerValue() { + if (type == INTEGER) { + return ((Long) value).longValue(); + } + + if (type == FLOAT) { + return ((Double) value).longValue(); + } + + if (type == BOOLEAN) { + return ((Boolean) value).booleanValue() ? 1 : 0; + } + + try { + return Long.parseLong(getStringValue()); + } catch (Exception x) { + return 0; + } } - public double getFloatValue () { - if (type == FLOAT) - return ((Double) value).doubleValue(); - if (type == INTEGER) - return ((Long) value).doubleValue (); - try { - return Double.parseDouble (getStringValue()); - } catch (Exception x) { - return 0.0; - } + /** + * + * + * @return ... + */ + public double getFloatValue() { + if (type == FLOAT) { + return ((Double) value).doubleValue(); + } + + if (type == INTEGER) { + return ((Long) value).doubleValue(); + } + + try { + return Double.parseDouble(getStringValue()); + } catch (Exception x) { + return 0.0; + } } + /** + * + * + * @return ... + */ + public Date getDateValue() { + if (type == DATE) { + return (Date) value; + } - public Date getDateValue () { - if (type == DATE) - return (Date) value; - return null; + return null; } - public Timestamp getTimestampValue () { - if (type == DATE && value != null) - return new Timestamp (((Date) value).getTime()); - return null; + /** + * + * + * @return ... + */ + public Timestamp getTimestampValue() { + if ((type == DATE) && (value != null)) { + return new Timestamp(((Date) value).getTime()); + } + + return null; } - public boolean getBooleanValue () { - if (type == BOOLEAN) - return ((Boolean) value).booleanValue(); - if (type == INTEGER) - return !(0 == getIntegerValue()); - return false; + /** + * + * + * @return ... + */ + public boolean getBooleanValue() { + if (type == BOOLEAN) { + return ((Boolean) value).booleanValue(); + } + + if (type == INTEGER) { + return !(0 == getIntegerValue()); + } + + return false; } - public INode getNodeValue () { - if (type == NODE && value != null) { - NodeHandle nhandle = (NodeHandle) value; - return nhandle.getNode (node.nmgr); - } - return null; + /** + * + * + * @return ... + */ + public INode getNodeValue() { + if ((type == NODE) && (value != null)) { + NodeHandle nhandle = (NodeHandle) value; + + return nhandle.getNode(node.nmgr); + } + + return null; } - public Object getJavaObjectValue () { - if (type == JAVAOBJECT) - return value; - return null; - } + /** + * + * + * @return ... + */ + public Object getJavaObjectValue() { + if (type == JAVAOBJECT) { + return value; + } + return null; + } } - - diff --git a/src/helma/objectmodel/db/Relation.java b/src/helma/objectmodel/db/Relation.java index b744e588..1a93f497 100644 --- a/src/helma/objectmodel/db/Relation.java +++ b/src/helma/objectmodel/db/Relation.java @@ -1,13 +1,26 @@ -// Relation.java -// Copyright (c) Hannes Wallnöfer 1997-2000 +/* + * 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 helma.objectmodel.*; import helma.framework.core.Application; +import helma.objectmodel.*; +import java.sql.SQLException; import java.util.Properties; import java.util.Vector; -import java.sql.SQLException; /** * This describes how a property of a persistent Object is stored in a @@ -15,23 +28,25 @@ import java.sql.SQLException; * or a reference to one or more other objects. */ public final class Relation { - // these constants define different type of property-to-db-mappings - // there is an error in the description of this relation public final static int INVALID = -1; - // a mapping of a non-object, scalar type + + // a mapping of a non-object, scalar type public final static int PRIMITIVE = 0; - // a 1-to-1 relation, i.e. a field in the table is a foreign key to another object + + // a 1-to-1 relation, i.e. a field in the table is a foreign key to another object public final static int REFERENCE = 1; + // a 1-to-many relation, a field in another table points to objects of this type public final static int COLLECTION = 2; + // direct mapping is a very powerful feature: objects of some types can be directly accessed // by one of their properties/db fields. // public final static int DIRECT = 3; - // the DbMapping of the type we come from DbMapping ownType; + // the DbMapping of the prototype we link to, unless this is a "primitive" (non-object) relation DbMapping otherType; @@ -40,20 +55,15 @@ public final class Relation { // if this relation defines a virtual node, we need to provide a DbMapping for these virtual nodes DbMapping virtualMapping; - String propName; String columnName; - int reftype; - Constraint[] constraints; - boolean virtual; boolean readonly; boolean aggressiveLoading; boolean aggressiveCaching; boolean isPrivate; - String accessName; // db column used to access objects through this relation String order; String groupbyOrder; @@ -63,187 +73,222 @@ public final class Relation { String filter; int maxSize = 0; - /** * This constructor makes a copy of an existing relation. Not all fields are copied, just those * which are needed in groupby- and virtual nodes defined by this relation. */ - public Relation (Relation rel) { - this.ownType = rel.ownType; - this.otherType = rel.otherType; - this.propName = rel.propName; - this.columnName = rel.columnName; - this.reftype = rel.reftype; - this.constraints = rel.constraints; - this.accessName = rel.accessName; - this.maxSize = rel.maxSize; + public Relation(Relation rel) { + this.ownType = rel.ownType; + this.otherType = rel.otherType; + this.propName = rel.propName; + this.columnName = rel.columnName; + this.reftype = rel.reftype; + this.constraints = rel.constraints; + this.accessName = rel.accessName; + this.maxSize = rel.maxSize; } /** * Reads a relation entry from a line in a properties file. */ - public Relation (String desc, String propName, DbMapping ownType, Properties props) { - this.ownType = ownType; - this.propName = propName; - otherType = null; + public Relation(String desc, String propName, DbMapping ownType, Properties props) { + this.ownType = ownType; + this.propName = propName; + otherType = null; } - //////////////////////////////////////////////////////////////////////////////////////////// // parse methods for new file format //////////////////////////////////////////////////////////////////////////////////////////// + public void update(String desc, Properties props) { + Application app = ownType.getApplication(); - public void update (String desc, Properties props) { - Application app = ownType.getApplication (); - if (desc == null || "".equals (desc.trim ())) { - if (propName != null) { - reftype = PRIMITIVE; - columnName = propName; - } else { - reftype = INVALID; - columnName = propName; - } - } else { - desc = desc.trim (); - int open = desc.indexOf ("("); - int close = desc.indexOf (")"); - if (open > -1 && close > open) { - String ref = desc.substring (0, open).trim (); - String proto = desc.substring (open+1, close).trim (); - if ("collection".equalsIgnoreCase (ref)) { - virtual = !"_children".equalsIgnoreCase (propName); - reftype = COLLECTION; - } else if ("mountpoint".equalsIgnoreCase (ref)) { - virtual = true; - reftype = COLLECTION; - prototype = proto; - } else if ("object".equalsIgnoreCase (ref)) { - virtual = false; - reftype = REFERENCE; - } else { - throw new RuntimeException ("Invalid property Mapping: "+desc); - } - otherType = app.getDbMapping (proto); - if (otherType == null) - throw new RuntimeException ("DbMapping for "+proto+" not found from "+ownType.typename); - } else { - virtual = false; - columnName = desc; - reftype = PRIMITIVE; - } - } - String rdonly = props.getProperty (propName+".readonly"); - if (rdonly != null && "true".equalsIgnoreCase (rdonly)) { - readonly = true; - } else { - readonly = false; - } - isPrivate = "true".equalsIgnoreCase (props.getProperty (propName+".private")); + if ((desc == null) || "".equals(desc.trim())) { + if (propName != null) { + reftype = PRIMITIVE; + columnName = propName; + } else { + reftype = INVALID; + columnName = propName; + } + } else { + desc = desc.trim(); - // the following options only apply to object and collection relations - if (reftype != PRIMITIVE && reftype != INVALID) { + int open = desc.indexOf("("); + int close = desc.indexOf(")"); - Vector newConstraints = new Vector (); - parseOptions (newConstraints, props); + if ((open > -1) && (close > open)) { + String ref = desc.substring(0, open).trim(); + String proto = desc.substring(open + 1, close).trim(); - constraints = new Constraint[newConstraints.size()]; - newConstraints.copyInto (constraints); - // if DbMapping for virtual nodes has already been created, - // update its subnode relation. - // FIXME: needs to be synchronized? - if (virtualMapping != null) { - virtualMapping.lastTypeChange = ownType.lastTypeChange; - virtualMapping.subRelation = getVirtualSubnodeRelation (); - virtualMapping.propRelation = getVirtualPropertyRelation (); - } - } + if ("collection".equalsIgnoreCase(ref)) { + virtual = !"_children".equalsIgnoreCase(propName); + reftype = COLLECTION; + } else if ("mountpoint".equalsIgnoreCase(ref)) { + virtual = true; + reftype = COLLECTION; + prototype = proto; + } else if ("object".equalsIgnoreCase(ref)) { + virtual = false; + reftype = REFERENCE; + } else { + throw new RuntimeException("Invalid property Mapping: " + desc); + } + + otherType = app.getDbMapping(proto); + + if (otherType == null) { + throw new RuntimeException("DbMapping for " + proto + + " not found from " + ownType.typename); + } + } else { + virtual = false; + columnName = desc; + reftype = PRIMITIVE; + } + } + + String rdonly = props.getProperty(propName + ".readonly"); + + if ((rdonly != null) && "true".equalsIgnoreCase(rdonly)) { + readonly = true; + } else { + readonly = false; + } + + isPrivate = "true".equalsIgnoreCase(props.getProperty(propName + ".private")); + + // the following options only apply to object and collection relations + if ((reftype != PRIMITIVE) && (reftype != INVALID)) { + Vector newConstraints = new Vector(); + + parseOptions(newConstraints, props); + + constraints = new Constraint[newConstraints.size()]; + newConstraints.copyInto(constraints); + + // if DbMapping for virtual nodes has already been created, + // update its subnode relation. + // FIXME: needs to be synchronized? + if (virtualMapping != null) { + virtualMapping.lastTypeChange = ownType.lastTypeChange; + virtualMapping.subRelation = getVirtualSubnodeRelation(); + virtualMapping.propRelation = getVirtualPropertyRelation(); + } + } } + protected void parseOptions(Vector cnst, Properties props) { + String loading = props.getProperty(propName + ".loadmode"); - protected void parseOptions (Vector cnst, Properties props) { - String loading = props.getProperty (propName+".loadmode"); - aggressiveLoading = loading != null && "aggressive".equalsIgnoreCase (loading.trim()); - String caching = props.getProperty (propName+".cachemode"); - aggressiveCaching = caching != null && "aggressive".equalsIgnoreCase (caching.trim()); - // get order property - order = props.getProperty (propName+".order"); - if (order != null && order.trim().length() == 0) - order = null; - // get additional filter property - filter = props.getProperty (propName+".filter"); - if (filter != null && filter.trim().length() == 0) - filter = null; - // get max size of collection - String max = props.getProperty (propName+".maxSize"); - if (max != null) try { - maxSize = Integer.parseInt (max); - } catch (NumberFormatException nfe) { - maxSize = 0; - } else { - maxSize = 0; - } - // get group by property - groupby = props.getProperty (propName+".group"); - if (groupby != null && groupby.trim().length() == 0) - groupby = null; - if (groupby != null) { - groupbyOrder = props.getProperty (propName+".group.order"); - if (groupbyOrder != null && groupbyOrder.trim().length() == 0) - groupbyOrder = null; - groupbyPrototype = props.getProperty (propName+".group.prototype"); - if (groupbyPrototype != null && groupbyPrototype.trim().length() == 0) - groupbyPrototype = null; - // aggressive loading and caching is not supported for groupby-nodes - aggressiveLoading = aggressiveCaching = false; - } - // check if subnode condition should be applied for property relations - accessName = props.getProperty (propName+".accessname"); - // parse contstraints - String local = props.getProperty (propName+".local"); - String foreign = props.getProperty (propName+".foreign"); - if (local != null && foreign != null) { - cnst.addElement (new Constraint (local, otherType.getTableName (), foreign, false)); - columnName = local; - } + aggressiveLoading = (loading != null) && + "aggressive".equalsIgnoreCase(loading.trim()); + + String caching = props.getProperty(propName + ".cachemode"); + + aggressiveCaching = (caching != null) && + "aggressive".equalsIgnoreCase(caching.trim()); + + // get order property + order = props.getProperty(propName + ".order"); + + if ((order != null) && (order.trim().length() == 0)) { + order = null; + } + + // get additional filter property + filter = props.getProperty(propName + ".filter"); + + if ((filter != null) && (filter.trim().length() == 0)) { + filter = null; + } + + // get max size of collection + String max = props.getProperty(propName + ".maxSize"); + + if (max != null) { + try { + maxSize = Integer.parseInt(max); + } catch (NumberFormatException nfe) { + maxSize = 0; + } + } else { + maxSize = 0; + } + + // get group by property + groupby = props.getProperty(propName + ".group"); + + if ((groupby != null) && (groupby.trim().length() == 0)) { + groupby = null; + } + + if (groupby != null) { + groupbyOrder = props.getProperty(propName + ".group.order"); + + if ((groupbyOrder != null) && (groupbyOrder.trim().length() == 0)) { + groupbyOrder = null; + } + + groupbyPrototype = props.getProperty(propName + ".group.prototype"); + + if ((groupbyPrototype != null) && (groupbyPrototype.trim().length() == 0)) { + groupbyPrototype = null; + } + + // aggressive loading and caching is not supported for groupby-nodes + aggressiveLoading = aggressiveCaching = false; + } + + // check if subnode condition should be applied for property relations + accessName = props.getProperty(propName + ".accessname"); + + // parse contstraints + String local = props.getProperty(propName + ".local"); + String foreign = props.getProperty(propName + ".foreign"); + + if ((local != null) && (foreign != null)) { + cnst.addElement(new Constraint(local, otherType.getTableName(), foreign, false)); + columnName = local; + } } - /////////////////////////////////////////////////////////////////////////////////////////// /** * Does this relation describe a virtual (collection) node? */ - public boolean isVirtual () { - return virtual; + public boolean isVirtual() { + return virtual; } /** * Tell if this relation represents a primitive (scalar) value mapping. */ - public boolean isPrimitive () { - return reftype == PRIMITIVE; + public boolean isPrimitive() { + return reftype == PRIMITIVE; } /** * Returns true if this Relation describes an object reference property */ - public boolean isReference () { - return reftype == REFERENCE; + public boolean isReference() { + return reftype == REFERENCE; } /** * Returns true if this Relation describes a collection object property */ - public boolean isCollection () { - return reftype == COLLECTION; + public boolean isCollection() { + return reftype == COLLECTION; } /** * 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. */ - public boolean isPrivate () { - return isPrivate; + public boolean isPrivate() { + return isPrivate; } /** @@ -252,8 +297,8 @@ public final class Relation { * node. Virtual nodes are objects which are only generated on demand * and never stored to a persistent storage. */ - public boolean createPropertyOnDemand () { - return virtual || accessName != null || groupby != null; + public boolean createPropertyOnDemand() { + return virtual || (accessName != null) || (groupby != null); } /** @@ -264,355 +309,470 @@ public final class Relation { * object stored in the db, since a virtual collection would lose its * its content after restarts. */ - public boolean needsPersistence () { - if (!virtual) - return false; - if (prototype == null) - return !otherType.isRelational (); - DbMapping sub = otherType.getSubnodeMapping (); - return sub != null && !sub.isRelational (); + public boolean needsPersistence() { + if (!virtual) { + return false; + } + + if (prototype == null) { + return !otherType.isRelational(); + } + + DbMapping sub = otherType.getSubnodeMapping(); + + return (sub != null) && !sub.isRelational(); } /** * Return the prototype to be used for object reached by this relation */ - public String getPrototype () { - return prototype; + public String getPrototype() { + return prototype; } /** * Return the name of the local property this relation is defined for */ - public String getPropName () { - return propName; + public String getPropName() { + return propName; } - public void setColumnType (int ct) { - columnType = ct; + /** + * + * + * @param ct ... + */ + public void setColumnType(int ct) { + columnType = ct; } - public int getColumnType () { - return columnType; + /** + * + * + * @return ... + */ + public int getColumnType() { + return columnType; } - /** * Add a constraint to the current list of constraints */ - protected void addConstraint (Constraint c) { - if (constraints == null) { - constraints = new Constraint[1]; - constraints[0] = c; - } else { - Constraint[] nc = new Constraint[constraints.length+1]; - System.arraycopy (constraints, 0, nc, 0, constraints.length); - nc[nc.length-1] = c; - constraints = nc; - } + protected void addConstraint(Constraint c) { + if (constraints == null) { + constraints = new Constraint[1]; + constraints[0] = c; + } else { + Constraint[] nc = new Constraint[constraints.length + 1]; + + System.arraycopy(constraints, 0, nc, 0, constraints.length); + nc[nc.length - 1] = c; + constraints = nc; + } } + /** + * + * + * @return ... + */ + public boolean usesPrimaryKey() { + if (otherType != null) { + if (reftype == REFERENCE) { + return (constraints.length == 1) && constraints[0].foreignKeyIsPrimary(); + } - public boolean usesPrimaryKey () { - if (otherType != null) { - if (reftype == REFERENCE) - return constraints.length == 1 && constraints[0].foreignKeyIsPrimary (); - if (reftype == COLLECTION) - return accessName == null || accessName.equalsIgnoreCase (otherType.getIDField ()); - } - return false; + if (reftype == COLLECTION) { + return (accessName == null) || + accessName.equalsIgnoreCase(otherType.getIDField()); + } + } + + return false; } - public boolean hasAccessName () { - return accessName != null; + /** + * + * + * @return ... + */ + public boolean hasAccessName() { + return accessName != null; } - public String getAccessName () { - return accessName; + /** + * + * + * @return ... + */ + public String getAccessName() { + return accessName; } - public Relation getSubnodeRelation () { - // return subnoderelation; - return null; + /** + * + * + * @return ... + */ + public Relation getSubnodeRelation() { + // return subnoderelation; + return null; } - /** * Return the local field name for updates. */ - public String getDbField () { - return columnName; + public String getDbField() { + return columnName; } - /** * get a DbMapping to use for virtual aka collection nodes. */ - public DbMapping getVirtualMapping () { - // return null unless this relation describes a virtual/collection node. - if (!virtual) - return null; - // if the collection node is prototyped, return the app's DbMapping - // for that prototype - if (prototype != null) { - return otherType; - } - // create a synthetic DbMapping that describes how to fetch the - // collection's child objects. - if (virtualMapping == null) { - virtualMapping = new DbMapping (ownType.app); - virtualMapping.subRelation = getVirtualSubnodeRelation (); - virtualMapping.propRelation = getVirtualPropertyRelation (); - } - return virtualMapping; - } + public DbMapping getVirtualMapping() { + // return null unless this relation describes a virtual/collection node. + if (!virtual) { + return null; + } + // if the collection node is prototyped, return the app's DbMapping + // for that prototype + if (prototype != null) { + return otherType; + } + + // create a synthetic DbMapping that describes how to fetch the + // collection's child objects. + if (virtualMapping == null) { + virtualMapping = new DbMapping(ownType.app); + virtualMapping.subRelation = getVirtualSubnodeRelation(); + virtualMapping.propRelation = getVirtualPropertyRelation(); + } + + return virtualMapping; + } /** * Return a Relation that defines the subnodes of a virtual node. */ - Relation getVirtualSubnodeRelation () { - if (!virtual) - throw new RuntimeException ("getVirtualSubnodeRelation called on non-virtual relation"); - Relation vr = new Relation (this); - vr.groupby = groupby; - vr.groupbyOrder = groupbyOrder; - vr.groupbyPrototype = groupbyPrototype; - vr.order = order; - vr.filter = filter; - vr.maxSize = maxSize; - vr.constraints = constraints; - vr.aggressiveLoading = aggressiveLoading; - vr.aggressiveCaching = aggressiveCaching; - return vr; + Relation getVirtualSubnodeRelation() { + if (!virtual) { + throw new RuntimeException("getVirtualSubnodeRelation called on non-virtual relation"); + } + + Relation vr = new Relation(this); + + vr.groupby = groupby; + vr.groupbyOrder = groupbyOrder; + vr.groupbyPrototype = groupbyPrototype; + vr.order = order; + vr.filter = filter; + vr.maxSize = maxSize; + vr.constraints = constraints; + vr.aggressiveLoading = aggressiveLoading; + vr.aggressiveCaching = aggressiveCaching; + + return vr; } /** * Return a Relation that defines the properties of a virtual node. */ - Relation getVirtualPropertyRelation () { - if (!virtual) - throw new RuntimeException ("getVirtualPropertyRelation called on non-virtual relation"); - Relation vr = new Relation (this); - vr.groupby = groupby; - vr.groupbyOrder = groupbyOrder; - vr.groupbyPrototype = groupbyPrototype; - vr.order = order; - vr.filter = filter; - vr.maxSize = maxSize; - vr.constraints = constraints; - return vr; + Relation getVirtualPropertyRelation() { + if (!virtual) { + throw new RuntimeException("getVirtualPropertyRelation called on non-virtual relation"); + } + + Relation vr = new Relation(this); + + vr.groupby = groupby; + vr.groupbyOrder = groupbyOrder; + vr.groupbyPrototype = groupbyPrototype; + vr.order = order; + vr.filter = filter; + vr.maxSize = maxSize; + vr.constraints = constraints; + + return vr; } /** * Return a Relation that defines the subnodes of a group-by node. */ - Relation getGroupbySubnodeRelation () { - if (groupby == null) - throw new RuntimeException ("getGroupbySubnodeRelation called on non-group-by relation"); - Relation vr = new Relation (this); - vr.order = order; - vr.prototype = groupbyPrototype; - vr.filter = filter; - vr.constraints = constraints; - vr.addConstraint (new Constraint (null, null, groupby, true)); - vr.aggressiveLoading = aggressiveLoading; - vr.aggressiveCaching = aggressiveCaching; - return vr; + Relation getGroupbySubnodeRelation() { + if (groupby == null) { + throw new RuntimeException("getGroupbySubnodeRelation called on non-group-by relation"); + } + + Relation vr = new Relation(this); + + vr.order = order; + vr.prototype = groupbyPrototype; + vr.filter = filter; + vr.constraints = constraints; + vr.addConstraint(new Constraint(null, null, groupby, true)); + vr.aggressiveLoading = aggressiveLoading; + vr.aggressiveCaching = aggressiveCaching; + + return vr; } /** * Return a Relation that defines the properties of a group-by node. */ - Relation getGroupbyPropertyRelation () { - if (groupby == null) - throw new RuntimeException ("getGroupbyPropertyRelation called on non-group-by relation"); - Relation vr = new Relation (this); - vr.order = order; - vr.prototype = groupbyPrototype; - vr.filter = filter; - vr.constraints = constraints; - vr.addConstraint (new Constraint (null, null, groupby, true)); - return vr; - } + Relation getGroupbyPropertyRelation() { + if (groupby == null) { + throw new RuntimeException("getGroupbyPropertyRelation called on non-group-by relation"); + } + Relation vr = new Relation(this); + + vr.order = order; + vr.prototype = groupbyPrototype; + vr.filter = filter; + vr.constraints = constraints; + vr.addConstraint(new Constraint(null, null, groupby, true)); + + return vr; + } /** * Build the second half of an SQL select statement according to this relation * and a local object. */ - public String buildQuery (INode home, INode nonvirtual, String kstr, String pre, boolean useOrder) throws SQLException { - StringBuffer q = new StringBuffer (); - String prefix = pre; - if (kstr != null) { - q.append (prefix); - String accessColumn = accessName == null ? otherType.getIDField () : accessName; - q.append (accessColumn); - q.append (" = "); - // check if column is string type and value needs to be quoted - if (otherType.needsQuotes (accessColumn)) { - q.append ("'"); - q.append (escape (kstr)); - q.append ("'"); - } else - q.append (escape (kstr)); - prefix = " AND "; - } - for (int i=0; i child.created()) - return false; - for (int i=0; i child.created())) { + return false; + } + for (int i = 0; i < constraints.length; i++) { + String propname = constraints[i].foreignProperty(); + + if (propname != null) { + INode home = constraints[i].isGroupby ? parent + : parent.getNonVirtualParent(); + String localName = constraints[i].localName; + String value = null; + + if ((localName == null) || + localName.equalsIgnoreCase(ownType.getIDField())) { + value = home.getID(); + } else if (ownType.isRelational()) { + value = home.getString(ownType.columnNameToProperty(localName)); + } else { + value = home.getString(localName); + } + + if ((value != null) && !value.equals(child.getString(propname))) { + return false; + } + } + } + + return true; + } /** * Make sure that the child node fullfills the constraints defined by this relation by setting the * appropriate properties */ - public void setConstraints (Node parent, Node child) { - INode home = parent.getNonVirtualParent (); - for (int i=0; i"+otherType+"]" + c; + /** + * + * + * @return ... + */ + public String toString() { + String c = ""; + + if (constraints != null) { + for (int i = 0; i < constraints.length; i++) + c += constraints[i].toString(); + } + + return "Relation[" + ownType + "." + propName + ">" + otherType + "]" + c; } /** @@ -620,55 +780,59 @@ public final class Relation { * establish a relation between database mapped objects. */ class Constraint { + String localName; + String tableName; + String foreignName; + boolean isGroupby; - String localName; - String tableName; - String foreignName; - boolean isGroupby; + Constraint(String local, String table, String foreign, boolean groupby) { + localName = local; + tableName = table; + foreignName = foreign; + isGroupby = groupby; + } - Constraint (String local, String table, String foreign, boolean groupby) { - localName = local; - tableName = table; - foreignName = foreign; - isGroupby = groupby; - } + public void addToQuery(StringBuffer q, INode home, INode nonvirtual) + throws SQLException { + String local = null; + INode ref = isGroupby ? home : nonvirtual; - public void addToQuery (StringBuffer q, INode home, INode nonvirtual) throws SQLException { - String local = null; - INode ref = isGroupby ? home : nonvirtual; - if (localName == null || localName.equalsIgnoreCase (ref.getDbMapping ().getIDField ())) - local = ref.getID (); - else { - String homeprop = ownType.columnNameToProperty (localName); - local = ref.getString (homeprop); - } - q.append (foreignName); - q.append (" = "); - if (otherType.needsQuotes (foreignName)) { - q.append ("'"); - q.append (escape (local)); - q.append ("'"); - } else - q.append (escape (local)); - } + if ((localName == null) || + localName.equalsIgnoreCase(ref.getDbMapping().getIDField())) { + local = ref.getID(); + } else { + String homeprop = ownType.columnNameToProperty(localName); - public boolean foreignKeyIsPrimary () { - return foreignName == null || foreignName.equalsIgnoreCase (otherType.getIDField ()); - } + local = ref.getString(homeprop); + } - public String foreignProperty () { - return otherType.columnNameToProperty (foreignName); - } + q.append(foreignName); + q.append(" = "); - public String localProperty () { - return ownType.columnNameToProperty (localName); - } + if (otherType.needsQuotes(foreignName)) { + q.append("'"); + q.append(escape(local)); + q.append("'"); + } else { + q.append(escape(local)); + } + } - public String toString () { - return ownType+"."+localName+"="+tableName+"."+foreignName; - } + public boolean foreignKeyIsPrimary() { + return (foreignName == null) || + foreignName.equalsIgnoreCase(otherType.getIDField()); + } + + public String foreignProperty() { + return otherType.columnNameToProperty(foreignName); + } + + public String localProperty() { + return ownType.columnNameToProperty(localName); + } + + public String toString() { + return ownType + "." + localName + "=" + tableName + "." + foreignName; + } } - } - - diff --git a/src/helma/objectmodel/db/Replicator.java b/src/helma/objectmodel/db/Replicator.java index d8d72cde..82669f3c 100644 --- a/src/helma/objectmodel/db/Replicator.java +++ b/src/helma/objectmodel/db/Replicator.java @@ -1,6 +1,19 @@ -// Replicator.java -// Copyright (c) Hannes Wallnöfer 2001 - +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ + package helma.objectmodel.db; import java.rmi.*; @@ -9,76 +22,113 @@ import java.util.*; /** * This class replicates the updates of transactions to other applications via RMI */ - public class Replicator implements Runnable { - Vector urls; - Vector add, delete, currentAdd, currentDelete; + Vector add; + Vector delete; + Vector currentAdd; + Vector currentDelete; Thread runner; NodeManager nmgr; - public Replicator (NodeManager nmgr) { - urls = new Vector (); - add = new Vector (); - delete = new Vector (); - this.nmgr = nmgr; - runner = new Thread (this); - runner.start (); + /** + * Creates a new Replicator object. + * + * @param nmgr ... + */ + public Replicator(NodeManager nmgr) { + urls = new Vector(); + add = new Vector(); + delete = new Vector(); + this.nmgr = nmgr; + runner = new Thread(this); + runner.start(); } - public void addUrl (String url) { - urls.addElement (url); - if (nmgr.logReplication) - nmgr.app.logEvent ("Adding replication listener: "+url); + /** + * + * + * @param url ... + */ + public void addUrl(String url) { + urls.addElement(url); + + if (nmgr.logReplication) { + nmgr.app.logEvent("Adding replication listener: " + url); + } } - public void run () { - while (Thread.currentThread () == runner) { - if (prepareReplication ()) { - for (int i=0; i\n"); - out.write ("\n"); - out.write ("\n" ); - out.write ("\n"); - out.write (" " + idgen.getValue() + "\n"); - out.write ("\n"); - out.close (); - return idgen; - } - -} + /** + * + * + * @param idgen ... + * @param file ... + * + * @return ... + * + * @throws Exception ... + */ + public static IDGenerator saveIDGenerator(IDGenerator idgen, File file) + throws IOException { + OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file)); + out.write("\n"); + out.write("\n"); + out.write("\n"); + out.write("\n"); + out.write(" " + idgen.getValue() + "\n"); + out.write("\n"); + out.close(); + + return idgen; + } +} diff --git a/src/helma/objectmodel/dom/XmlConstants.java b/src/helma/objectmodel/dom/XmlConstants.java index 2ab0709d..7c87064c 100644 --- a/src/helma/objectmodel/dom/XmlConstants.java +++ b/src/helma/objectmodel/dom/XmlConstants.java @@ -1,8 +1,25 @@ +/* + * 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.dom; -public interface XmlConstants { - - public final String NAMESPACE = "http://www.helma.org/docs/guide/features/database"; - public final String DATEFORMAT = "dd.MM.yyyy HH:mm:ss z"; - +/** + * + */ +public interface XmlConstants { + public final String NAMESPACE = "http://www.helma.org/docs/guide/features/database"; + public final String DATEFORMAT = "dd.MM.yyyy HH:mm:ss z"; } diff --git a/src/helma/objectmodel/dom/XmlConverter.java b/src/helma/objectmodel/dom/XmlConverter.java index 51c6472e..95e1bed6 100644 --- a/src/helma/objectmodel/dom/XmlConverter.java +++ b/src/helma/objectmodel/dom/XmlConverter.java @@ -1,345 +1,536 @@ +/* + * 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.dom; -import java.io.*; -import java.net.*; -import java.util.*; - -import javax.xml.parsers.*; -import org.w3c.dom.*; -import org.xml.sax.InputSource; - import helma.objectmodel.*; import helma.util.SystemProperties; +import org.w3c.dom.*; +import org.xml.sax.InputSource; +import java.io.*; +import java.net.*; +import java.util.*; +import javax.xml.parsers.*; +/** + * + */ public class XmlConverter implements XmlConstants { - private boolean DEBUG = false; - private boolean sparse = false; - private Properties props; - - private char defaultSeparator = '_'; - + private char defaultSeparator = '_'; private int offset = 0; + /** + * Creates a new XmlConverter object. + */ public XmlConverter() { - props = new SystemProperties(); + props = new SystemProperties(); } + /** + * Creates a new XmlConverter object. + * + * @param propFile ... + */ public XmlConverter(String propFile) { - props = new SystemProperties(propFile); - extractProperties(props); + props = new SystemProperties(propFile); + extractProperties(props); } + /** + * Creates a new XmlConverter object. + * + * @param propFile ... + */ public XmlConverter(File propFile) { - this ( propFile.getAbsolutePath() ); + this(propFile.getAbsolutePath()); } + /** + * Creates a new XmlConverter object. + * + * @param props ... + */ public XmlConverter(Properties props) { - this.props = props; - extractProperties(props); + this.props = props; + extractProperties(props); } - public INode convert( String desc ) { - return convert(desc, new TransientNode() ); + /** + * + * + * @param desc ... + * + * @return ... + */ + public INode convert(String desc) { + return convert(desc, new TransientNode()); } - public INode convert( String desc, INode helmaNode ) throws RuntimeException { - try { - return convert( new URL(desc), helmaNode ); - } catch ( MalformedURLException notanurl ) { - try { - return convert( new File(desc), helmaNode ); - } catch ( FileNotFoundException notfound ) { - throw new RuntimeException( "couldn't read xml: " + desc ); - } - } catch ( IOException ioerror ) { - throw new RuntimeException( "couldn't read xml: " + desc ); - } + /** + * + * + * @param desc ... + * @param helmaNode ... + * + * @return ... + * + * @throws RuntimeException ... + */ + public INode convert(String desc, INode helmaNode) + throws RuntimeException { + try { + return convert(new URL(desc), helmaNode); + } catch (MalformedURLException notanurl) { + try { + return convert(new File(desc), helmaNode); + } catch (FileNotFoundException notfound) { + throw new RuntimeException("couldn't read xml: " + desc); + } + } catch (IOException ioerror) { + throw new RuntimeException("couldn't read xml: " + desc); + } } - public INode convert( File file, INode helmaNode ) throws RuntimeException, FileNotFoundException { - return convert( new FileInputStream(file), helmaNode ); + /** + * + * + * @param file ... + * @param helmaNode ... + * + * @return ... + * + * @throws RuntimeException ... + * @throws FileNotFoundException ... + */ + public INode convert(File file, INode helmaNode) + throws RuntimeException, FileNotFoundException { + return convert(new FileInputStream(file), helmaNode); } - public INode convert( URL url, INode helmaNode ) throws RuntimeException, IOException, MalformedURLException { - return convert( url.openConnection().getInputStream(), helmaNode ); + /** + * + * + * @param url ... + * @param helmaNode ... + * + * @return ... + * + * @throws RuntimeException ... + * @throws IOException ... + * @throws MalformedURLException ... + */ + public INode convert(URL url, INode helmaNode) + throws RuntimeException, IOException, MalformedURLException { + return convert(url.openConnection().getInputStream(), helmaNode); } - public INode convert( InputStream in, INode helmaNode ) throws RuntimeException { - Document document = XmlUtil.parse (in); - if ( document!=null && document.getDocumentElement()!=null ) { - return convert( document.getDocumentElement(), helmaNode, new HashMap() ); - } else { - return helmaNode; - } + /** + * + * + * @param in ... + * @param helmaNode ... + * + * @return ... + * + * @throws RuntimeException ... + */ + public INode convert(InputStream in, INode helmaNode) + throws RuntimeException { + Document document = XmlUtil.parse(in); + + if ((document != null) && (document.getDocumentElement() != null)) { + return convert(document.getDocumentElement(), helmaNode, new HashMap()); + } else { + return helmaNode; + } } - public INode convertFromString( String xml, INode helmaNode ) throws RuntimeException { - Document document = XmlUtil.parse (new InputSource (new StringReader (xml))); - if ( document!=null && document.getDocumentElement()!=null ) { - return convert( document.getDocumentElement(), helmaNode, new HashMap() ); - } else { - return helmaNode; - } + /** + * + * + * @param xml ... + * @param helmaNode ... + * + * @return ... + * + * @throws RuntimeException ... + */ + public INode convertFromString(String xml, INode helmaNode) + throws RuntimeException { + Document document = XmlUtil.parse(new InputSource(new StringReader(xml))); + + if ((document != null) && (document.getDocumentElement() != null)) { + return convert(document.getDocumentElement(), helmaNode, new HashMap()); + } else { + return helmaNode; + } } - public INode convert( Element element, INode helmaNode, Map nodeCache ) { - offset++; - // previousNode is used to cache previous nodes with the same prototype - // so we can reset it in the nodeCache after we've run - Object previousNode = null; - if (DEBUG) - debug("reading " + element.getNodeName() ); - String prototype = props.getProperty(element.getNodeName()+"._prototype"); - if ( prototype == null && !sparse ) - prototype = "HopObject"; - // if we have a prototype (either explicit or implicit "hopobject"), - // set it on the Helma node and store it in the node cache. - if ( prototype != null ) { - helmaNode.setName( element.getNodeName() ); - helmaNode.setPrototype( prototype ); - previousNode = nodeCache.put (prototype, helmaNode); - } - // check attributes of the current element - attributes(element, helmaNode, nodeCache); - // check child nodes of the current element - if ( element.hasChildNodes() ) - children(element, helmaNode, nodeCache); + /** + * + * + * @param element ... + * @param helmaNode ... + * @param nodeCache ... + * + * @return ... + */ + public INode convert(Element element, INode helmaNode, Map nodeCache) { + offset++; - // if it exists, restore the previous node we've replaced in the node cache. - if (previousNode != null) - nodeCache.put (prototype, previousNode); + // previousNode is used to cache previous nodes with the same prototype + // so we can reset it in the nodeCache after we've run + Object previousNode = null; - offset--; - return helmaNode; + if (DEBUG) { + debug("reading " + element.getNodeName()); + } + + String prototype = props.getProperty(element.getNodeName() + "._prototype"); + + if ((prototype == null) && !sparse) { + prototype = "HopObject"; + } + + // if we have a prototype (either explicit or implicit "hopobject"), + // set it on the Helma node and store it in the node cache. + if (prototype != null) { + helmaNode.setName(element.getNodeName()); + helmaNode.setPrototype(prototype); + previousNode = nodeCache.put(prototype, helmaNode); + } + + // check attributes of the current element + attributes(element, helmaNode, nodeCache); + + // check child nodes of the current element + if (element.hasChildNodes()) { + children(element, helmaNode, nodeCache); + } + + // if it exists, restore the previous node we've replaced in the node cache. + if (previousNode != null) { + nodeCache.put(prototype, previousNode); + } + + offset--; + + return helmaNode; } /** * parse xml children and create hopobject-children */ - private INode children( Element element, helma.objectmodel.INode helmaNode, Map nodeCache ) { - NodeList list = element.getChildNodes(); - int len = list.getLength(); - boolean nodeIsInitialized = !nodeCache.isEmpty(); - StringBuffer textcontent = new StringBuffer(); - String domKey, helmaKey; - for ( int i=0; i append to StringBuffer - if ( childNode.getNodeType() == Node.TEXT_NODE || - childNode.getNodeType() == Node.CDATA_SECTION_NODE ) { - textcontent.append( childNode.getNodeValue().trim() ); - continue; - } + if (helmaNode.getPrototype() != null) { + return helmaNode; + } + } - // it's some kind of element (property or child) - if ( childNode.getNodeType() == Node.ELEMENT_NODE ) { + continue; + } - Element childElement = (Element)childNode; + // if it's text content of this element -> append to StringBuffer + if ((childNode.getNodeType() == Node.TEXT_NODE) || + (childNode.getNodeType() == Node.CDATA_SECTION_NODE)) { + textcontent.append(childNode.getNodeValue().trim()); - // get the basic key we have to look for in the properties-table - domKey = element.getNodeName()+"."+childElement.getNodeName(); + continue; + } - // is there a childtext-2-property mapping? - if ( props!=null && props.containsKey(domKey+"._text") ) { - helmaKey = props.getProperty(domKey+"._text"); - if( helmaKey.equals("") ) - // if property is set but without value, read elementname for this mapping - helmaKey = childElement.getNodeName().replace(':',defaultSeparator); - if (DEBUG) - debug("childtext-2-property mapping, helmaKey " + helmaKey + " for domKey " + domKey ); - // check if helmaKey contains an explicit prototype name in which to - // set the property. - int dot = helmaKey.indexOf("."); - if (dot > -1) { - String prototype = helmaKey.substring (0, dot); - INode node = (INode) nodeCache.get (prototype); - helmaKey = helmaKey.substring (dot+1); - if (node != null && node.getString(helmaKey)==null) { - node.setString (helmaKey, XmlUtil.getTextContent (childNode)); - } - } else if ( helmaNode.getString(helmaKey)==null ) { - helmaNode.setString( helmaKey, XmlUtil.getTextContent(childNode) ); - if (DEBUG) - debug("childtext-2-property mapping, setting " + helmaKey + " as string" ); - } - continue; - } + // it's some kind of element (property or child) + if (childNode.getNodeType() == Node.ELEMENT_NODE) { + Element childElement = (Element) childNode; - // is there a simple child-2-property mapping? - // (lets the user define to use only one element and make this a property - // and simply ignore other elements of the same name) - if ( props!=null && props.containsKey(domKey+"._property") ) { - helmaKey = props.getProperty(domKey+"._property"); - // if property is set but without value, read elementname for this mapping: - if ( helmaKey.equals("") ) - helmaKey = childElement.getNodeName().replace(':',defaultSeparator); - if (DEBUG) - debug("child-2-property mapping, helmaKey " + helmaKey + " for domKey " + domKey); - // get the node on which to opererate, depending on the helmaKey - // value from the properties file. - INode node = helmaNode; - int dot = helmaKey.indexOf("."); - if (dot > -1) { - String prototype = helmaKey.substring (0, dot); - if (!prototype.equalsIgnoreCase (node.getPrototype())) - node = (INode) nodeCache.get (prototype); - helmaKey = helmaKey.substring (dot+1); - } - if (node == null) - continue; + // get the basic key we have to look for in the properties-table + domKey = element.getNodeName() + "." + childElement.getNodeName(); - if ( node.getNode(helmaKey)==null ) { - convert( childElement, node.createNode(helmaKey), nodeCache ); - if (DEBUG) - debug( "read " + childElement.toString() + node.getNode(helmaKey).toString() ); - } - continue; - } + // is there a childtext-2-property mapping? + if ((props != null) && props.containsKey(domKey + "._text")) { + helmaKey = props.getProperty(domKey + "._text"); + if (helmaKey.equals("")) { + // if property is set but without value, read elementname for this mapping + helmaKey = childElement.getNodeName().replace(':', + defaultSeparator); + } - // map it to one of the children-lists - helma.objectmodel.INode newHelmaNode = null; - String childrenMapping = props.getProperty(element.getNodeName()+"._children"); - // do we need a mapping directly among _children of helmaNode? - // can either be through property elname._children=_all or elname._children=childname - if( childrenMapping!=null && ( childrenMapping.equals("_all") || childrenMapping.equals(childElement.getNodeName()) ) ) { - newHelmaNode = convert(childElement, helmaNode.createNode(null), nodeCache ); - } - // in which virtual subnode collection should objects of this type be stored? - helmaKey = props.getProperty(domKey); - if ( helmaKey==null && !sparse ) { - helmaKey = childElement.getNodeName().replace(':',defaultSeparator); - } - if (helmaKey == null) { - // we don't map this child element itself since we do - // sparse parsing, but there may be something of interest - // in the child's attributes and child elements. - attributes (childElement, helmaNode, nodeCache); - children (childElement, helmaNode, nodeCache); - continue; - } + if (DEBUG) { + debug("childtext-2-property mapping, helmaKey " + helmaKey + + " for domKey " + domKey); + } - // get the node on which to opererate, depending on the helmaKey - // value from the properties file. - INode node = helmaNode; - int dot = helmaKey.indexOf("."); - if (dot > -1) { - String prototype = helmaKey.substring (0, dot); - if (!prototype.equalsIgnoreCase (node.getPrototype())) - node = (INode) nodeCache.get (prototype); - helmaKey = helmaKey.substring (dot+1); - } - if (node == null) - continue; + // check if helmaKey contains an explicit prototype name in which to + // set the property. + int dot = helmaKey.indexOf("."); - // try to get the virtual node - INode worknode = null; - if ("_children".equals (helmaKey)) { - worknode = node; - } else { - worknode = node.getNode( helmaKey ); - if ( worknode==null ) { - // if virtual node doesn't exist, create it - worknode = helmaNode.createNode( helmaKey ); - } - } - if (DEBUG) - debug( "mounting child "+ childElement.getNodeName() + " at worknode " + worknode.toString() ); - // now mount it, possibly re-using the helmaNode that's been created before - if ( newHelmaNode!=null ) { - worknode.addNode(newHelmaNode); - } else { - convert( childElement, worknode.createNode( null ), nodeCache ); - } - } - // forget about other types (comments etc) - continue; - } + if (dot > -1) { + String prototype = helmaKey.substring(0, dot); + INode node = (INode) nodeCache.get(prototype); - // if there's some text content for this element, map it: - if ( textcontent.length()>0 && !sparse ) { - helmaKey = props.getProperty(element.getNodeName()+"._text"); - if ( helmaKey==null ) - helmaKey = "text"; - if (DEBUG) - debug ("setting text "+textcontent+" to property "+helmaKey+" of object "+helmaNode); - helmaNode.setString(helmaKey, textcontent.toString().trim() ); - } + helmaKey = helmaKey.substring(dot + 1); - return helmaNode; + if ((node != null) && (node.getString(helmaKey) == null)) { + node.setString(helmaKey, XmlUtil.getTextContent(childNode)); + } + } else if (helmaNode.getString(helmaKey) == null) { + helmaNode.setString(helmaKey, XmlUtil.getTextContent(childNode)); + + if (DEBUG) { + debug("childtext-2-property mapping, setting " + helmaKey + + " as string"); + } + } + + continue; + } + + // is there a simple child-2-property mapping? + // (lets the user define to use only one element and make this a property + // and simply ignore other elements of the same name) + if ((props != null) && props.containsKey(domKey + "._property")) { + helmaKey = props.getProperty(domKey + "._property"); + + // if property is set but without value, read elementname for this mapping: + if (helmaKey.equals("")) { + helmaKey = childElement.getNodeName().replace(':', + defaultSeparator); + } + + if (DEBUG) { + debug("child-2-property mapping, helmaKey " + helmaKey + + " for domKey " + domKey); + } + + // get the node on which to opererate, depending on the helmaKey + // value from the properties file. + INode node = helmaNode; + int dot = helmaKey.indexOf("."); + + if (dot > -1) { + String prototype = helmaKey.substring(0, dot); + + if (!prototype.equalsIgnoreCase(node.getPrototype())) { + node = (INode) nodeCache.get(prototype); + } + + helmaKey = helmaKey.substring(dot + 1); + } + + if (node == null) { + continue; + } + + if (node.getNode(helmaKey) == null) { + convert(childElement, node.createNode(helmaKey), nodeCache); + + if (DEBUG) { + debug("read " + childElement.toString() + + node.getNode(helmaKey).toString()); + } + } + + continue; + } + + // map it to one of the children-lists + helma.objectmodel.INode newHelmaNode = null; + String childrenMapping = props.getProperty(element.getNodeName() + + "._children"); + + // do we need a mapping directly among _children of helmaNode? + // can either be through property elname._children=_all or elname._children=childname + if ((childrenMapping != null) && + (childrenMapping.equals("_all") || + childrenMapping.equals(childElement.getNodeName()))) { + newHelmaNode = convert(childElement, helmaNode.createNode(null), + nodeCache); + } + + // in which virtual subnode collection should objects of this type be stored? + helmaKey = props.getProperty(domKey); + + if ((helmaKey == null) && !sparse) { + helmaKey = childElement.getNodeName().replace(':', defaultSeparator); + } + + if (helmaKey == null) { + // we don't map this child element itself since we do + // sparse parsing, but there may be something of interest + // in the child's attributes and child elements. + attributes(childElement, helmaNode, nodeCache); + children(childElement, helmaNode, nodeCache); + + continue; + } + + // get the node on which to opererate, depending on the helmaKey + // value from the properties file. + INode node = helmaNode; + int dot = helmaKey.indexOf("."); + + if (dot > -1) { + String prototype = helmaKey.substring(0, dot); + + if (!prototype.equalsIgnoreCase(node.getPrototype())) { + node = (INode) nodeCache.get(prototype); + } + + helmaKey = helmaKey.substring(dot + 1); + } + + if (node == null) { + continue; + } + + // try to get the virtual node + INode worknode = null; + + if ("_children".equals(helmaKey)) { + worknode = node; + } else { + worknode = node.getNode(helmaKey); + + if (worknode == null) { + // if virtual node doesn't exist, create it + worknode = helmaNode.createNode(helmaKey); + } + } + + if (DEBUG) { + debug("mounting child " + childElement.getNodeName() + + " at worknode " + worknode.toString()); + } + + // now mount it, possibly re-using the helmaNode that's been created before + if (newHelmaNode != null) { + worknode.addNode(newHelmaNode); + } else { + convert(childElement, worknode.createNode(null), nodeCache); + } + } + + // forget about other types (comments etc) + continue; + } + + // if there's some text content for this element, map it: + if ((textcontent.length() > 0) && !sparse) { + helmaKey = props.getProperty(element.getNodeName() + "._text"); + + if (helmaKey == null) { + helmaKey = "text"; + } + + if (DEBUG) { + debug("setting text " + textcontent + " to property " + helmaKey + + " of object " + helmaNode); + } + + helmaNode.setString(helmaKey, textcontent.toString().trim()); + } + + return helmaNode; } /** * set element's attributes as properties of helmaNode */ - private INode attributes( Element element, INode helmaNode, Map nodeCache ) { - NamedNodeMap nnm = element.getAttributes(); - int len = nnm.getLength(); - for ( int i=0; i -1) { - String prototype = helmaKey.substring (0, dot); - INode node = (INode) nodeCache.get (prototype); - if (node != null) { - node.setString (helmaKey.substring(dot+1), attr.getNodeValue()); - } - } else if (helmaNode.getPrototype() != null) { - helmaNode.setString( helmaKey, attr.getNodeValue() ); - } - } - } - return helmaNode; + private INode attributes(Element element, INode helmaNode, Map nodeCache) { + NamedNodeMap nnm = element.getAttributes(); + int len = nnm.getLength(); + + for (int i = 0; i < len; i++) { + org.w3c.dom.Node attr = nnm.item(i); + String helmaKey = props.getProperty(element.getNodeName() + "._attribute." + + attr.getNodeName()); + + // unless we only map explicit attributes, use attribute name as property name + // in case no property name was defined. + if ((helmaKey == null) && !sparse) { + helmaKey = attr.getNodeName().replace(':', defaultSeparator); + } + + if (helmaKey != null) { + // check if the mapping contains the prototype to which + // the property should be applied + int dot = helmaKey.indexOf("."); + + if (dot > -1) { + String prototype = helmaKey.substring(0, dot); + INode node = (INode) nodeCache.get(prototype); + + if (node != null) { + node.setString(helmaKey.substring(dot + 1), attr.getNodeValue()); + } + } else if (helmaNode.getPrototype() != null) { + helmaNode.setString(helmaKey, attr.getNodeValue()); + } + } + } + + return helmaNode; } /** * utility function */ - private void extractProperties( Properties props ) { - if ( props.containsKey("separator") ) { - defaultSeparator = props.getProperty("separator").charAt(0); - } - sparse = "sparse".equalsIgnoreCase (props.getProperty("_mode")); - } + private void extractProperties(Properties props) { + if (props.containsKey("separator")) { + defaultSeparator = props.getProperty("separator").charAt(0); + } + sparse = "sparse".equalsIgnoreCase(props.getProperty("_mode")); + } /** for testing */ void debug(Object msg) { - for ( int i=0; i"); - else - writeln (""); - // writeln (""); - // writeln ("" ); - write (""); - write (node, null, null, 0); - writeln (""); - convertedNodes = null; - return true; - } + /** + * starting point for printing a node tree. + * creates document header too and initializes + * the cache of already converted nodes. + */ + public boolean write(INode node) throws IOException { + convertedNodes = new Vector(); - /** - * write a hopobject and print all its properties and children. - * references are made here if a node already has been fully printed - * or if this is the last level that's going to be dumped - */ - public void write (INode node, String elementName, String propName, int level) throws IOException { - if (node==null) - return; - // if (stopTypes != null && stopTypes.contains (node.getPrototype())) - // return; - int previousLength = prefix.length(); - prefix.append(indent); - if ( ++level>maxLevels ) { - writeReferenceTag (node, elementName, propName); - prefix.setLength( previousLength ); - return; - } - if ( convertedNodes.contains(node) ) { - writeReferenceTag (node, elementName, propName); - } else { - convertedNodes.addElement (node); - writeTagOpen (node, elementName, propName); - INode parent = node.getParent (); - if ( parent!=null ) { - writeReferenceTag (parent, "hop:parent", null); - } - writeProperties (node, level); - writeChildren (node, level); - writeTagClose (elementName); - } - prefix.setLength( previousLength ); - } + if (explicitEncoding == null) { + writeln(""); + } else { + writeln(""); + } + // writeln (""); + // writeln ("" ); + write(""); + write(node, null, null, 0); + writeln(""); + convertedNodes = null; + return true; + } - /** - * loop through properties and print them with their property-name - * as elementname - */ - private void writeProperties (INode node, int level) throws IOException { - Enumeration e = null; - if ( dbmode==true && node instanceof helma.objectmodel.db.Node ) { - // a newly constructed db.Node doesn't have a propMap, - // but returns an enumeration of all it's db-mapped properties - Hashtable props = ((Node)node).getPropMap(); - if (props==null) - return; - e = props.keys(); - } else { - e = node.properties(); - } - while ( e.hasMoreElements() ) { - String key = (String)e.nextElement(); - IProperty prop = node.get(key); - if ( prop!=null ) { - boolean validName = isValidElementName (key); - String elementName, propName; - if (validName) { - elementName = key; - propName = null; - } else { - elementName = "property"; - propName = key; - } - int type = prop.getType(); - if( type==IProperty.NODE ) { - write (prop.getNodeValue(), elementName, propName, level); - } else { - writeProperty (prop, elementName, propName); - } - } - } - } + /** + * write a hopobject and print all its properties and children. + * references are made here if a node already has been fully printed + * or if this is the last level that's going to be dumped + */ + public void write(INode node, String elementName, String propName, int level) + throws IOException { + if (node == null) { + return; + } - /* public void writeNullProperty (String key) throws IOException { - write (prefix.toString()); - write (indent); - write ("<"); - write (key); - write (" type=\"null\"/>"); - write (LINESEPARATOR); - } */ + // if (stopTypes != null && stopTypes.contains (node.getPrototype())) + // return; + int previousLength = prefix.length(); - /** - * write a single property, set attribute type according to type, - * apply xml-encoding. - */ - public void writeProperty (IProperty property, String elementName, String propName) throws IOException { - int propType = property.getType(); - // we can't encode java objects in XML - if (propType == IProperty.JAVAOBJECT) - return; - write (prefix.toString()); - write (indent); - write ("<"); - write (elementName); - if (propName != null) { - write (" propertyname=\""); - write (HtmlEncoder.encodeXml (propName)); - write ("\""); - } - switch (propType) { - case IProperty.BOOLEAN: - write (" type=\"boolean\">"); - write (property.getStringValue()); - break; - case IProperty.FLOAT: - write (" type=\"float\">"); - write (property.getStringValue()); - break; - case IProperty.INTEGER: - write (" type=\"integer\">"); - write (property.getStringValue()); - break; - case IProperty.DATE: - write (" type=\"date\">"); - write ( format.format (property.getDateValue()) ); - break; - case IProperty.STRING: - write (">"); - String str = HtmlEncoder.encodeXml (property.getStringValue()); - if (str != null) - write ( str ); - } - write (""); - write (LINESEPARATOR); - } + prefix.append(indent); - /** - * loop through the children-array and print them as - */ - private void writeChildren (INode node, int level) throws IOException { - if ( dbmode==true && node instanceof helma.objectmodel.db.Node ) { - Node dbNode = (Node)node; - DbMapping smap = dbNode.getDbMapping() == null ? null : dbNode.getDbMapping().getSubnodeMapping (); - if (smap != null && smap.isRelational ()) - return; - } - Enumeration e = node.getSubnodes(); - while (e.hasMoreElements()) { - INode nextNode = (INode)e.nextElement(); - write (nextNode, "hop:child", null, level); - } - } + if (++level > maxLevels) { + writeReferenceTag(node, elementName, propName); + prefix.setLength(previousLength); - /** - * write an opening tag for a node. Include id and prototype, use a - * name if parameter is non-empty. - */ - public void writeTagOpen (INode node, String name, String propName) throws IOException { - write (prefix.toString()); - write ("<"); - write ( (name==null)?"hopobject" : name); - write (" id=\""); - write (getNodeIdentifier(node)); - if (propName != null) { - write ("\" propertyname=\""); - write (HtmlEncoder.encodeXml (propName)); - } - write ("\" name=\""); - write (node.getName()); - write ("\" prototype=\""); - write (getNodePrototype(node)); - write ("\" created=\""); - write (Long.toString(node.created())); - write ("\" lastModified=\""); - write (Long.toString(node.lastModified())); - //FIXME: do we need anonymous-property? - write ("\">"); - write (LINESEPARATOR); - } + return; + } - /** - * write a closing tag for a node - * e.g. - */ - public void writeTagClose (String name) throws IOException { - write (prefix.toString()); - write (""); - write (LINESEPARATOR); - } + if (convertedNodes.contains(node)) { + writeReferenceTag(node, elementName, propName); + } else { + convertedNodes.addElement(node); + writeTagOpen(node, elementName, propName); - /** - * write a tag holding a reference to an element that has - * been written out before. - * e.g. - */ - public void writeReferenceTag (INode node, String name, String propName) throws IOException { - write (prefix.toString()); - write ("<"); - write ( (name==null)?"hopobject" : name); - write ( " idref=\""); - write (getNodeIdentifier(node)); - if (propName != null) { - write ("\" propertyname=\""); - write (HtmlEncoder.encodeXml (propName)); - } - write ("\" prototyperef=\""); - write (getNodePrototype(node)); - write ("\"/>"); - write (LINESEPARATOR); - } + INode parent = node.getParent(); - /** - * retrieve prototype-string of a node, defaults to "hopobject" - */ - private String getNodePrototype( INode node ) { - if ( node.getPrototype()==null || "".equals(node.getPrototype()) ) { - return "hopobject"; - } else { - return node.getPrototype(); - } - } + if (parent != null) { + writeReferenceTag(parent, "hop:parent", null); + } - /** - * TransientNode produces a different ID each time we call the getID()-method - * this is a workaround and uses hashCode if INode stands for a TransientNode. - */ - private String getNodeIdentifier( INode node ) { - try { - TransientNode tmp = (TransientNode)node; - return Integer.toString( tmp.hashCode() ); - } catch ( ClassCastException e ) { - return node.getID(); - } - } + writeProperties(node, level); + writeChildren(node, level); + writeTagClose(elementName); + } - public void writeln(String str) throws IOException { - write (str); - write (LINESEPARATOR); - } + prefix.setLength(previousLength); + } - - /** - * Check if a string is usable as XML element name. If not, the name - * will be appended as attribute to the XML element. We are - * conservative here, preferring to return false rather too often than - * not enough. - */ - private boolean isValidElementName (String str) { - char c = str.charAt (0); - if (!Character.isLetter(c)) - return false; - int l = str.length(); - for (int i=1; i"); + write (LINESEPARATOR); + } */ + + /** + * write a single property, set attribute type according to type, + * apply xml-encoding. + */ + public void writeProperty(IProperty property, String elementName, String propName) + throws IOException { + int propType = property.getType(); + + // we can't encode java objects in XML + if (propType == IProperty.JAVAOBJECT) { + return; + } + + write(prefix.toString()); + write(indent); + write("<"); + write(elementName); + + if (propName != null) { + write(" propertyname=\""); + write(HtmlEncoder.encodeXml(propName)); + write("\""); + } + + switch (propType) { + case IProperty.BOOLEAN: + write(" type=\"boolean\">"); + write(property.getStringValue()); + + break; + + case IProperty.FLOAT: + write(" type=\"float\">"); + write(property.getStringValue()); + + break; + + case IProperty.INTEGER: + write(" type=\"integer\">"); + write(property.getStringValue()); + + break; + + case IProperty.DATE: + write(" type=\"date\">"); + write(format.format(property.getDateValue())); + + break; + + case IProperty.STRING: + write(">"); + + String str = HtmlEncoder.encodeXml(property.getStringValue()); + + if (str != null) { + write(str); + } + } + + write(""); + write(LINESEPARATOR); + } + + /** + * loop through the children-array and print them as + */ + private void writeChildren(INode node, int level) throws IOException { + if ((dbmode == true) && node instanceof helma.objectmodel.db.Node) { + Node dbNode = (Node) node; + DbMapping smap = (dbNode.getDbMapping() == null) ? null + : dbNode.getDbMapping() + .getSubnodeMapping(); + + if ((smap != null) && smap.isRelational()) { + return; + } + } + + Enumeration e = node.getSubnodes(); + + while (e.hasMoreElements()) { + INode nextNode = (INode) e.nextElement(); + + write(nextNode, "hop:child", null, level); + } + } + + /** + * write an opening tag for a node. Include id and prototype, use a + * name if parameter is non-empty. + */ + public void writeTagOpen(INode node, String name, String propName) + throws IOException { + write(prefix.toString()); + write("<"); + write((name == null) ? "hopobject" : name); + write(" id=\""); + write(getNodeIdentifier(node)); + + if (propName != null) { + write("\" propertyname=\""); + write(HtmlEncoder.encodeXml(propName)); + } + + write("\" name=\""); + write(node.getName()); + write("\" prototype=\""); + write(getNodePrototype(node)); + write("\" created=\""); + write(Long.toString(node.created())); + write("\" lastModified=\""); + write(Long.toString(node.lastModified())); + + //FIXME: do we need anonymous-property? + write("\">"); + write(LINESEPARATOR); + } + + /** + * write a closing tag for a node + * e.g. + */ + public void writeTagClose(String name) throws IOException { + write(prefix.toString()); + write(""); + write(LINESEPARATOR); + } + + /** + * write a tag holding a reference to an element that has + * been written out before. + * e.g. + */ + public void writeReferenceTag(INode node, String name, String propName) + throws IOException { + write(prefix.toString()); + write("<"); + write((name == null) ? "hopobject" : name); + write(" idref=\""); + write(getNodeIdentifier(node)); + + if (propName != null) { + write("\" propertyname=\""); + write(HtmlEncoder.encodeXml(propName)); + } + + write("\" prototyperef=\""); + write(getNodePrototype(node)); + write("\"/>"); + write(LINESEPARATOR); + } + + /** + * retrieve prototype-string of a node, defaults to "hopobject" + */ + private String getNodePrototype(INode node) { + if ((node.getPrototype() == null) || "".equals(node.getPrototype())) { + return "hopobject"; + } else { + return node.getPrototype(); + } + } + + /** + * TransientNode produces a different ID each time we call the getID()-method + * this is a workaround and uses hashCode if INode stands for a TransientNode. + */ + private String getNodeIdentifier(INode node) { + try { + TransientNode tmp = (TransientNode) node; + + return Integer.toString(tmp.hashCode()); + } catch (ClassCastException e) { + return node.getID(); + } + } + + /** + * + * + * @param str ... + * + * @throws IOException ... + */ + public void writeln(String str) throws IOException { + write(str); + write(LINESEPARATOR); + } + + /** + * Check if a string is usable as XML element name. If not, the name + * will be appended as attribute to the XML element. We are + * conservative here, preferring to return false rather too often than + * not enough. + */ + private boolean isValidElementName(String str) { + char c = str.charAt(0); + + if (!Character.isLetter(c)) { + return false; + } + + int l = str.length(); + + for (int i = 1; i < l; i++) { + c = str.charAt(i); + + if (!Character.isLetterOrDigit(c) && (c != '-') && (c != '_')) { + return false; + } + } + + return true; + } } - diff --git a/src/helma/scripting/ActionFile.java b/src/helma/scripting/ActionFile.java index fa4e8c49..bb1e6957 100644 --- a/src/helma/scripting/ActionFile.java +++ b/src/helma/scripting/ActionFile.java @@ -1,15 +1,27 @@ -// ActionFile.java -// Copyright (c) Helma.org 1998-2002 +/* + * 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.scripting; -import java.util.Vector; -import java.util.Iterator; -import java.io.*; import helma.framework.*; import helma.framework.core.*; import helma.util.Updatable; - +import java.io.*; +import java.util.Iterator; +import java.util.Vector; /** * An ActionFile is a file containing function code that is exposed as a URI @@ -17,114 +29,178 @@ import helma.util.Updatable; * usually represented by a file with extension .hac (hop action file) * that contains the raw body of the function. */ - - public class ActionFile implements Updatable { - - String name, sourceName; + String name; + String sourceName; Prototype prototype; Application app; File file; String content; long lastmod; - - public ActionFile (File file, String name, Prototype proto) { - this.prototype = proto; - this.app = proto.getApplication (); - this.name = name; - this.sourceName = file.getParentFile().getName()+"/"+file.getName(); - this.file = file; - this.lastmod = file.lastModified (); - this.content = null; + /** + * Creates a new ActionFile object. + * + * @param file ... + * @param name ... + * @param proto ... + */ + public ActionFile(File file, String name, Prototype proto) { + this.prototype = proto; + this.app = proto.getApplication(); + this.name = name; + this.sourceName = file.getParentFile().getName() + "/" + file.getName(); + this.file = file; + this.lastmod = file.lastModified(); + this.content = null; } - public ActionFile (String content, String name, String sourceName, Prototype proto) { - this.prototype = proto; - this.app = proto.getApplication (); - this.name = name; - this.sourceName = sourceName; - this.file = null; - this.content = content; + /** + * Creates a new ActionFile object. + * + * @param content ... + * @param name ... + * @param sourceName ... + * @param proto ... + */ + public ActionFile(String content, String name, String sourceName, Prototype proto) { + this.prototype = proto; + this.app = proto.getApplication(); + this.name = name; + this.sourceName = sourceName; + this.file = null; + this.content = content; } - /** * Tell the type manager whether we need an update. this is the case when * the file has been modified or deleted. */ - public boolean needsUpdate () { - return lastmod != file.lastModified (); + public boolean needsUpdate() { + return lastmod != file.lastModified(); } - - public void update () { - if (!file.exists ()) { - // remove functions declared by this from all object prototypes - remove (); - } else { - lastmod = file.lastModified (); - } + /** + * + */ + public void update() { + if (!file.exists()) { + // remove functions declared by this from all object prototypes + remove(); + } else { + lastmod = file.lastModified(); + } } - public void remove () { - prototype.removeActionFile (this); + /** + * + */ + public void remove() { + prototype.removeActionFile(this); } - public File getFile () { - return file; + /** + * + * + * @return ... + */ + public File getFile() { + return file; } - public String getName () { - return name; + /** + * + * + * @return ... + */ + public String getName() { + return name; } - public String getSourceName () { - return sourceName; + /** + * + * + * @return ... + */ + public String getSourceName() { + return sourceName; } - public Reader getReader () throws FileNotFoundException { - if (content != null) - return new StringReader (content); - else if (file.length() == 0) - return new StringReader(";"); - else - return new FileReader (file); + /** + * + * + * @return ... + * + * @throws FileNotFoundException ... + */ + public Reader getReader() throws FileNotFoundException { + if (content != null) { + return new StringReader(content); + } else if (file.length() == 0) { + return new StringReader(";"); + } else { + return new FileReader(file); + } } - public String getContent () { - if (content != null) - return content; - else { - try { - FileReader reader = new FileReader (file); - char cbuf[] = new char[(int) file.length ()]; - reader.read (cbuf); - reader.close (); - return new String (cbuf); - } catch (Exception filex) { - app.logEvent ("Error reading "+this+": "+filex); - return null; - } - } + /** + * + * + * @return ... + */ + public String getContent() { + if (content != null) { + return content; + } else { + try { + FileReader reader = new FileReader(file); + char[] cbuf = new char[(int) file.length()]; + + reader.read(cbuf); + reader.close(); + + return new String(cbuf); + } catch (Exception filex) { + app.logEvent("Error reading " + this + ": " + filex); + + return null; + } + } } - public String getFunctionName () { - return name + "_action"; + /** + * + * + * @return ... + */ + public String getFunctionName() { + return name + "_action"; } - public Prototype getPrototype () { - return prototype; + /** + * + * + * @return ... + */ + public Prototype getPrototype() { + return prototype; } - public Application getApplication () { - return app; + /** + * + * + * @return ... + */ + public Application getApplication() { + return app; } - public String toString () { - return "ActionFile["+sourceName+"]"; + /** + * + * + * @return ... + */ + public String toString() { + return "ActionFile[" + sourceName + "]"; } - } - - diff --git a/src/helma/scripting/FunctionFile.java b/src/helma/scripting/FunctionFile.java index f89095d8..5c271414 100644 --- a/src/helma/scripting/FunctionFile.java +++ b/src/helma/scripting/FunctionFile.java @@ -1,25 +1,34 @@ -// FunctionFile.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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.scripting; -import java.util.HashMap; -import java.util.Iterator; -import java.util.HashSet; -import java.util.Enumeration; -import java.io.*; import helma.framework.*; import helma.framework.core.*; import helma.util.Updatable; - +import java.io.*; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; /** * This represents a File containing script functions for a given class/prototype. */ - - public class FunctionFile implements Updatable { - Prototype prototype; Application app; File file; @@ -27,13 +36,18 @@ public class FunctionFile implements Updatable { String content; long lastmod; - - public FunctionFile (File file, Prototype proto) { - this.prototype = proto; - this.app = proto.getApplication (); - this.sourceName = file.getParentFile().getName()+"/"+file.getName(); - this.file = file; - update (); + /** + * Creates a new FunctionFile object. + * + * @param file ... + * @param proto ... + */ + public FunctionFile(File file, Prototype proto) { + this.prototype = proto; + this.app = proto.getApplication(); + this.sourceName = file.getParentFile().getName() + "/" + file.getName(); + this.file = file; + update(); } /** @@ -41,55 +55,75 @@ public class FunctionFile implements Updatable { * files contained in zipped applications. The whole update mechanism is bypassed * by immediately parsing the code. */ - public FunctionFile (String body, String sourceName, Prototype proto) { - this.prototype = proto; - this.app = proto.getApplication (); - this.sourceName = sourceName; - this.file = null; - this.content = body; + public FunctionFile(String body, String sourceName, Prototype proto) { + this.prototype = proto; + this.app = proto.getApplication(); + this.sourceName = sourceName; + this.file = null; + this.content = body; } /** * Tell the type manager whether we need an update. this is the case when * the file has been modified or deleted. */ - public boolean needsUpdate () { - return file != null && lastmod != file.lastModified (); + public boolean needsUpdate() { + return (file != null) && (lastmod != file.lastModified()); } - - public void update () { - if (file != null) { - if (!file.exists ()) { - remove (); - } else { - lastmod = file.lastModified (); - } - } + /** + * + */ + public void update() { + if (file != null) { + if (!file.exists()) { + remove(); + } else { + lastmod = file.lastModified(); + } + } } - public File getFile () { - return file; + /** + * + * + * @return ... + */ + public File getFile() { + return file; } - public String getContent () { - return content; + /** + * + * + * @return ... + */ + public String getContent() { + return content; } - public String getSourceName () { - return sourceName; + /** + * + * + * @return ... + */ + public String getSourceName() { + return sourceName; } - public void remove () { - prototype.removeFunctionFile (this); + /** + * + */ + public void remove() { + prototype.removeFunctionFile(this); } - - public String toString () { - return sourceName; + /** + * + * + * @return ... + */ + public String toString() { + return sourceName; } - - } - - diff --git a/src/helma/scripting/ScriptingEngine.java b/src/helma/scripting/ScriptingEngine.java index ef7ace4d..d9320a57 100644 --- a/src/helma/scripting/ScriptingEngine.java +++ b/src/helma/scripting/ScriptingEngine.java @@ -1,5 +1,18 @@ -// ScriptingEngine.java -// Copyright (c) Hannes Wallnöfer 1998-2001 +/* + * 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.scripting; @@ -7,73 +20,69 @@ import helma.framework.IPathElement; import helma.framework.core.Application; import helma.framework.core.Prototype; import helma.framework.core.RequestEvaluator; -import java.util.*; import java.io.File; +import java.util.*; /** * This is the interface that must be implemented to make a scripting environment * usable by the Helma application server. * - * Implementations of this interface must have a public zero-argument constructor + * Implementations of this interface must have a public zero-argument constructor * to be usable by the Helma framework. */ public interface ScriptingEngine { - - /** + /** * Init the scripting engine with an application and a request evaluator */ - public void init (Application app, RequestEvaluator reval); + public void init(Application app, RequestEvaluator reval); /** * This method is called before an execution context for a request - * evaluation is entered to let the Engine know it should update + * evaluation is entered to let the Engine know it should update * its prototype information */ - public void updatePrototypes (); + public void updatePrototypes(); /** * This method is called when an execution context for a request * evaluation is entered. The globals parameter contains the global values * to be applied during this execution context. */ - public void enterContext (HashMap globals) throws ScriptingException; + public void enterContext(HashMap globals) throws ScriptingException; /** * This method is called to let the scripting engine know that the current * execution context has terminated. */ - public void exitContext (); - + public void exitContext(); /** - * Invoke a function on some object, using the given arguments and global vars. + * Invoke a function on some object, using the given arguments and global vars. * XML-RPC calls require special input and output parameter conversion. */ - public Object invoke (Object thisObject, String functionName, Object[] args, boolean xmlrpc) - throws ScriptingException; + public Object invoke(Object thisObject, String functionName, Object[] args, + boolean xmlrpc) throws ScriptingException; /** * Let the evaluator know that the current evaluation has been aborted. */ - public void abort (); + public void abort(); /** * Get a property on an object */ - public Object get (Object thisObject, String key); + public Object get(Object thisObject, String key); /** * Return true if a function by that name is defined for that object. */ - public boolean hasFunction (Object thisObject, String functionName); + public boolean hasFunction(Object thisObject, String functionName); /** * Get an IPathElement that offers introspection services into the application. * If this method returns null, no introspection is available for this kind of engine. - * In order to be compatible with the standard Helma management application, this + * In order to be compatible with the standard Helma management application, this * class should be compatible with helma.doc.DocApplication. */ - public IPathElement getIntrospector (); - + public IPathElement getIntrospector(); } - diff --git a/src/helma/scripting/ScriptingException.java b/src/helma/scripting/ScriptingException.java index 4cedd7ff..0e9f6f28 100644 --- a/src/helma/scripting/ScriptingException.java +++ b/src/helma/scripting/ScriptingException.java @@ -1,67 +1,105 @@ -// ScriptingException.java -// Copyright (c) Hannes Wallnöfer 1998-2001 +/* + * 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.scripting; - -import java.io.PrintWriter; import java.io.PrintStream; +import java.io.PrintWriter; /** * The base class for exceptions thrown by Helma scripting package */ public class ScriptingException extends Exception { - Exception wrapped; /** * Construct a ScriptingException given an error message */ - public ScriptingException (String msg) { - super (msg); - wrapped = null; + public ScriptingException(String msg) { + super(msg); + wrapped = null; } /** * Construct a ScriptingException given an error message */ - public ScriptingException (Exception w) { - wrapped = w; + public ScriptingException(Exception w) { + wrapped = w; } - public String toString () { - if (wrapped == null) - return super.toString (); - else - return wrapped.toString (); + /** + * + * + * @return ... + */ + public String toString() { + if (wrapped == null) { + return super.toString(); + } else { + return wrapped.toString(); + } } - public String getMessage () { - if (wrapped == null) - return super.getMessage (); - else - return wrapped.getMessage (); + /** + * + * + * @return ... + */ + public String getMessage() { + if (wrapped == null) { + return super.getMessage(); + } else { + return wrapped.getMessage(); + } } - public void printStackTrace () { - if (wrapped == null) - super.printStackTrace (); - else - wrapped.printStackTrace (); + /** + * + */ + public void printStackTrace() { + if (wrapped == null) { + super.printStackTrace(); + } else { + wrapped.printStackTrace(); + } } - public void printStackTrace (PrintStream stream) { - if (wrapped == null) - super.printStackTrace (stream); - else - wrapped.printStackTrace (stream); + /** + * + * + * @param stream ... + */ + public void printStackTrace(PrintStream stream) { + if (wrapped == null) { + super.printStackTrace(stream); + } else { + wrapped.printStackTrace(stream); + } } - public void printStackTrace (PrintWriter writer) { - if (wrapped == null) - super.printStackTrace (writer); - else - wrapped.printStackTrace (writer); + /** + * + * + * @param writer ... + */ + public void printStackTrace(PrintWriter writer) { + if (wrapped == null) { + super.printStackTrace(writer); + } else { + wrapped.printStackTrace(writer); + } } - } diff --git a/src/helma/scripting/Template.java b/src/helma/scripting/Template.java index 490112cc..72aa5044 100644 --- a/src/helma/scripting/Template.java +++ b/src/helma/scripting/Template.java @@ -1,187 +1,239 @@ -// Template.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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.scripting; -import java.io.*; -import java.util.Vector; -import java.util.Iterator; -import java.util.StringTokenizer; import helma.framework.*; import helma.framework.core.*; - +import java.io.*; +import java.util.Iterator; +import java.util.StringTokenizer; +import java.util.Vector; /** * This represents a Helma template, i.e. a file with the extension .hsp * (Helma server page) that contains both parts that are to be evaluated * as EcmaScript and parts that are to be delivered to the client as-is. - * Internally, templates are regular functions. + * Internally, templates are regular functions. * Helma templates are callable via URL, but this is just a leftover from the * days when there were no .hac (action) files. The recommended way * now is to have a .hac file with all the logic which in turn calls one or more * template files to do the formatting. */ - public class Template extends ActionFile { - - - public Template (File file, String name, Prototype proto) { - super (file, name, proto); + /** + * Creates a new Template object. + * + * @param file ... + * @param name ... + * @param proto ... + */ + public Template(File file, String name, Prototype proto) { + super(file, name, proto); } - public Template (String content, String name, String sourceName, Prototype proto) { - super (content, name, sourceName, proto); + /** + * Creates a new Template object. + * + * @param content ... + * @param name ... + * @param sourceName ... + * @param proto ... + */ + public Template(String content, String name, String sourceName, Prototype proto) { + super(content, name, sourceName, proto); } - public String getFunctionName () { - return name; + /** + * + * + * @return ... + */ + public String getFunctionName() { + return name; } - public Reader getReader () { - return new StringReader(getContent()); + /** + * + * + * @return ... + */ + public Reader getReader() { + return new StringReader(getContent()); } - public String getContent () { + /** + * + * + * @return ... + */ + public String getContent() { + Vector partBuffer = new Vector(); + String cstring = super.getContent(); + char[] cnt = cstring.toCharArray(); + int l = cnt.length; - Vector partBuffer = new Vector (); - String cstring = super.getContent(); - char[] cnt = cstring.toCharArray (); - int l = cnt.length; - if (l == 0) - return ""; + if (l == 0) { + return ""; + } - // if last charackter is whitespace, swallow it. this is necessary for some inner templates to look ok. - if (Character.isWhitespace (cnt[l-1])) - l -= 1; + // if last charackter is whitespace, swallow it. this is necessary for some inner templates to look ok. + if (Character.isWhitespace(cnt[l - 1])) { + l -= 1; + } - int lastIdx = 0; - for (int i = 0; i < l-1; i++) { - if (cnt[i] == '<' && cnt[i+1] == '%') { - int j = i+2; - while (j < l-1 && (cnt[j] != '%' || cnt[j+1] != '>')) { - j++; - } - if (j > i+2) { - if (i - lastIdx > 0) - partBuffer.addElement (new Part (this, new String (cnt, lastIdx, i - lastIdx), true)); - String script = new String (cnt, i+2, (j-i)-2); - partBuffer.addElement (new Part (this, script, false)); - lastIdx = j+2; - } - i = j+1; - } - } - if (lastIdx < l) - partBuffer.addElement (new Part (this, new String (cnt, lastIdx, l - lastIdx), true)); + int lastIdx = 0; - StringBuffer templateBody = new StringBuffer (); - int nparts = partBuffer.size(); + for (int i = 0; i < (l - 1); i++) { + if ((cnt[i] == '<') && (cnt[i + 1] == '%')) { + int j = i + 2; - for (int k = 0; k < nparts; k++) { - Part nextPart = (Part) partBuffer.elementAt (k); + while ((j < (l - 1)) && ((cnt[j] != '%') || (cnt[j + 1] != '>'))) { + j++; + } - if (nextPart.isStatic || nextPart.content.trim ().startsWith ("=")) { - // check for <%= ... %> statements - if (!nextPart.isStatic) { - nextPart.content = nextPart.content.trim ().substring (1).trim (); - // cut trailing ";" - while (nextPart.content.endsWith (";")) - nextPart.content = nextPart.content.substring (0, nextPart.content.length()-1); - } + if (j > (i + 2)) { + if ((i - lastIdx) > 0) { + partBuffer.addElement(new Part(this, + new String(cnt, lastIdx, + i - lastIdx), true)); + } - StringTokenizer st = new StringTokenizer (nextPart.content, "\r\n", true); - String nextLine = st.hasMoreTokens () ? st.nextToken () : null; + String script = new String(cnt, i + 2, (j - i) - 2); - // count newLines we "swallow", see explanation below - int newLineCount = 0; + partBuffer.addElement(new Part(this, script, false)); + lastIdx = j + 2; + } - templateBody.append ("res.write ("); - if (nextPart.isStatic) { - templateBody.append ("\""); - } + i = j + 1; + } + } - while (nextLine != null) { + if (lastIdx < l) { + partBuffer.addElement(new Part(this, new String(cnt, lastIdx, l - lastIdx), + true)); + } - if ("\n".equals (nextLine)) { - // append a CRLF - newLineCount++; - templateBody.append ("\\r\\n"); - } else if (!"\r".equals (nextLine)) try { - StringReader lineReader = new StringReader (nextLine); - int c = lineReader.read (); - while (c > -1) { - if (nextPart.isStatic && ((char)c == '"' || (char)c == '\\')) { - templateBody.append ('\\'); - } - templateBody.append ((char) c); - c = lineReader.read (); - } - } catch (IOException srx) {} + StringBuffer templateBody = new StringBuffer(); + int nparts = partBuffer.size(); - nextLine = st.hasMoreTokens () ? st.nextToken () : null; + for (int k = 0; k < nparts; k++) { + Part nextPart = (Part) partBuffer.elementAt(k); - } + if (nextPart.isStatic || nextPart.content.trim().startsWith("=")) { + // check for <%= ... %> statements + if (!nextPart.isStatic) { + nextPart.content = nextPart.content.trim().substring(1).trim(); - if (nextPart.isStatic) { - templateBody.append ("\""); - } + // cut trailing ";" + while (nextPart.content.endsWith(";")) + nextPart.content = nextPart.content.substring(0, + nextPart.content.length() - + 1); + } - templateBody.append ("); "); + StringTokenizer st = new StringTokenizer(nextPart.content, "\r\n", true); + String nextLine = st.hasMoreTokens() ? st.nextToken() : null; - // append the number of lines we have "swallowed" into - // one write statement, so error messages will *approximately* - // give correct line numbers. - for (int i=0; i -1) { + if (nextPart.isStatic && + (((char) c == '"') || ((char) c == '\\'))) { + templateBody.append('\\'); + } + + templateBody.append((char) c); + c = lineReader.read(); + } + } catch (IOException srx) { + } + } + + nextLine = st.hasMoreTokens() ? st.nextToken() : null; + } + + if (nextPart.isStatic) { + templateBody.append("\""); + } + + templateBody.append("); "); + + // append the number of lines we have "swallowed" into + // one write statement, so error messages will *approximately* + // give correct line numbers. + for (int i = 0; i < newLineCount; i++) { + templateBody.append("\r\n"); + } + } else { + templateBody.append(nextPart.content); + + if (!nextPart.content.trim().endsWith(";")) { + templateBody.append(";"); + } + } + } + + // templateBody.append ("\r\nreturn null;\r\n"); + return templateBody.toString(); } - public void remove () { - prototype.removeTemplate (this); + /** + * + */ + public void remove() { + prototype.removeTemplate(this); } - - class Part { + String content; + Template parent; + boolean isPart; + boolean isStatic; - String content; - Template parent; - boolean isPart; - boolean isStatic; + public Part(Template parent, String content, boolean isStatic) { + isPart = false; + this.parent = parent; + this.content = content; + this.isStatic = isStatic; + } + public String getName() { + return isStatic ? null : content; + } - public Part (Template parent, String content, boolean isStatic) { - isPart = false; - this.parent = parent; - this.content = content; - this.isStatic = isStatic; - } - - public String getName () { - return isStatic ? null : content; - } - - - public String toString () { - return "Template.Part ["+content+","+isStatic+"]"; - } - + public String toString() { + return "Template.Part [" + content + "," + isStatic + "]"; + } } - } - - - - - diff --git a/src/helma/scripting/fesi/ESBeanWrapper.java b/src/helma/scripting/fesi/ESBeanWrapper.java index 35055157..f97daee2 100644 --- a/src/helma/scripting/fesi/ESBeanWrapper.java +++ b/src/helma/scripting/fesi/ESBeanWrapper.java @@ -1,65 +1,98 @@ +/* + * 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.scripting.fesi; -import java.util.Map; - -import helma.framework.core.ApplicationBean; -import helma.objectmodel.INode; -import helma.util.SystemProperties; - -import FESI.Interpreter.Evaluator; -import FESI.Exceptions.EcmaScriptException; import FESI.Data.ESNull; import FESI.Data.ESValue; import FESI.Data.ESWrapper; +import FESI.Exceptions.EcmaScriptException; +import FESI.Interpreter.Evaluator; +import helma.framework.core.ApplicationBean; +import helma.objectmodel.INode; +import helma.util.SystemProperties; +import java.util.Map; /** * Wrap a Java Bean for use in EcmaScript. */ - public class ESBeanWrapper extends ESWrapper { - FesiEngine engine; - public ESBeanWrapper (Object object, FesiEngine engine) { - super (object, engine.getEvaluator(),true); - this.engine = engine; + /** + * Creates a new ESBeanWrapper object. + * + * @param object ... + * @param engine ... + */ + public ESBeanWrapper(Object object, FesiEngine engine) { + super(object, engine.getEvaluator(), true); + this.engine = engine; } - /** - * Wrap getProperty, return ESNode if INode would be returned, - * ESMapWrapper if Map would be returned. - */ - public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException { - try { - ESValue val = super.getProperty (propertyName, hash); - if (val instanceof ESWrapper) { - Object theObject = ((ESWrapper)val).getJavaObject (); - if (val instanceof ESWrapper && theObject instanceof INode) { - return engine.getNodeWrapper ((INode) theObject); - } else if (val instanceof ESWrapper && theObject instanceof Map) { - ESMapWrapper wrapper = new ESMapWrapper(engine, (Map) theObject); - if (theObject instanceof SystemProperties && super.getJavaObject () instanceof ApplicationBean) - wrapper.setReadonly(true); - return wrapper; + /** + * Wrap getProperty, return ESNode if INode would be returned, + * ESMapWrapper if Map would be returned. + */ + public ESValue getProperty(String propertyName, int hash) + throws EcmaScriptException { + try { + ESValue val = super.getProperty(propertyName, hash); + + if (val instanceof ESWrapper) { + Object theObject = ((ESWrapper) val).getJavaObject(); + + if (val instanceof ESWrapper && theObject instanceof INode) { + return engine.getNodeWrapper((INode) theObject); + } else if (val instanceof ESWrapper && theObject instanceof Map) { + ESMapWrapper wrapper = new ESMapWrapper(engine, (Map) theObject); + + if (theObject instanceof SystemProperties && + super.getJavaObject() instanceof ApplicationBean) { + wrapper.setReadonly(true); + } + + return wrapper; + } } - } - return val; - } catch (Exception rte) { - return ESNull.theNull; - } - } - public void putProperty(String propertyName, ESValue propertyValue, int hash) throws EcmaScriptException { - try { - super.putProperty (propertyName, propertyValue, hash); - } catch (Exception rte) { - // create a nice error message - throw new EcmaScriptException("can't set property " + propertyName + - " to this value on " + getJavaObject().toString() ); - } + return val; + } catch (Exception rte) { + return ESNull.theNull; + } } + /** + * + * + * @param propertyName ... + * @param propertyValue ... + * @param hash ... + * + * @throws EcmaScriptException ... + */ + public void putProperty(String propertyName, ESValue propertyValue, int hash) + throws EcmaScriptException { + try { + super.putProperty(propertyName, propertyValue, hash); + } catch (Exception rte) { + // create a nice error message + throw new EcmaScriptException("can't set property " + propertyName + + " to this value on " + + getJavaObject().toString()); + } + } } - - - diff --git a/src/helma/scripting/fesi/ESGenericObject.java b/src/helma/scripting/fesi/ESGenericObject.java index c396bf74..6af94430 100644 --- a/src/helma/scripting/fesi/ESGenericObject.java +++ b/src/helma/scripting/fesi/ESGenericObject.java @@ -1,109 +1,236 @@ -// ESGenericObject.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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.scripting.fesi; -import helma.framework.core.*; -import helma.framework.IPathElement; -import FESI.Interpreter.*; -import FESI.Exceptions.*; import FESI.Data.*; +import FESI.Exceptions.*; +import FESI.Interpreter.*; +import helma.framework.IPathElement; +import helma.framework.core.*; import java.util.*; - /** - * A wrapper for a Java object that may or may not implement the IPathElement interface. - */ - + * A wrapper for a Java object that may or may not implement the IPathElement interface. + */ public class ESGenericObject extends ObjectPrototype { - ESWrapper wrapper; Object wrappedObject; - public ESGenericObject (ESObject prototype, Evaluator evaluator, Object obj) { - super (prototype, evaluator); - wrappedObject = obj; - wrapper = new ESWrapper (obj, evaluator); - } - - - public String getESClassName () { - return "GenericObject"; - } - - public String toString () { - return wrappedObject.toString (); - } - - public String toDetailString () { - return wrapper.toDetailString (); - } - - - public void putProperty(String propertyName, ESValue propertyValue, int hash) throws EcmaScriptException { - wrapper.putProperty (propertyName, propertyValue, hash); - } - - public boolean hasProperty(String propertyName, int hash) throws EcmaScriptException { - return super.hasProperty (propertyName, hash) || wrapper.hasProperty (propertyName, hash); - } - - public boolean deleteProperty(String propertyName, int hash) throws EcmaScriptException { - return wrapper.deleteProperty (propertyName, hash); - } - - public ESValue getProperty (int i) throws EcmaScriptException { - return wrapper.getProperty (i); - } - - public void putProperty(int index, ESValue propertyValue) throws EcmaScriptException { - wrapper.putProperty (index, propertyValue); - } - - - public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException { - ESValue val = super.getProperty (propertyName, hash); - if (val == null || val == ESUndefined.theUndefined) - val = wrapper.getProperty (propertyName, hash); - return val; - } - - public ESValue doIndirectCall(Evaluator evaluator, ESObject thisObject, String functionName, ESValue[] arguments) - throws EcmaScriptException, NoSuchMethodException { - if (super.hasProperty (functionName, functionName.hashCode())) - return super.doIndirectCall (evaluator, thisObject, functionName, arguments); - return wrapper.doIndirectCall (evaluator, thisObject, functionName, arguments); - } - - public Enumeration getAllProperties () { - return wrapper.getProperties (); - } - - public Enumeration getProperties () { - return wrapper.getProperties (); - } - - public Object toJavaObject () { - return wrappedObject; + /** + * Creates a new ESGenericObject object. + * + * @param prototype ... + * @param evaluator ... + * @param obj ... + */ + public ESGenericObject(ESObject prototype, Evaluator evaluator, Object obj) { + super(prototype, evaluator); + wrappedObject = obj; + wrapper = new ESWrapper(obj, evaluator); } /** - * An ESNode equals another object if it is an ESNode that wraps the same INode + * + * + * @return ... + */ + public String getESClassName() { + return "GenericObject"; + } + + /** + * + * + * @return ... + */ + public String toString() { + return wrappedObject.toString(); + } + + /** + * + * + * @return ... + */ + public String toDetailString() { + return wrapper.toDetailString(); + } + + /** + * + * + * @param propertyName ... + * @param propertyValue ... + * @param hash ... + * + * @throws EcmaScriptException ... + */ + public void putProperty(String propertyName, ESValue propertyValue, int hash) + throws EcmaScriptException { + wrapper.putProperty(propertyName, propertyValue, hash); + } + + /** + * + * + * @param propertyName ... + * @param hash ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public boolean hasProperty(String propertyName, int hash) + throws EcmaScriptException { + return super.hasProperty(propertyName, hash) || + wrapper.hasProperty(propertyName, hash); + } + + /** + * + * + * @param propertyName ... + * @param hash ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public boolean deleteProperty(String propertyName, int hash) + throws EcmaScriptException { + return wrapper.deleteProperty(propertyName, hash); + } + + /** + * + * + * @param i ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public ESValue getProperty(int i) throws EcmaScriptException { + return wrapper.getProperty(i); + } + + /** + * + * + * @param index ... + * @param propertyValue ... + * + * @throws EcmaScriptException ... + */ + public void putProperty(int index, ESValue propertyValue) + throws EcmaScriptException { + wrapper.putProperty(index, propertyValue); + } + + /** + * + * + * @param propertyName ... + * @param hash ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public ESValue getProperty(String propertyName, int hash) + throws EcmaScriptException { + ESValue val = super.getProperty(propertyName, hash); + + if ((val == null) || (val == ESUndefined.theUndefined)) { + val = wrapper.getProperty(propertyName, hash); + } + + return val; + } + + /** + * + * + * @param evaluator ... + * @param thisObject ... + * @param functionName ... + * @param arguments ... + * + * @return ... + * + * @throws EcmaScriptException ... + * @throws NoSuchMethodException ... + */ + public ESValue doIndirectCall(Evaluator evaluator, ESObject thisObject, + String functionName, ESValue[] arguments) + throws EcmaScriptException, NoSuchMethodException { + if (super.hasProperty(functionName, functionName.hashCode())) { + return super.doIndirectCall(evaluator, thisObject, functionName, arguments); + } + + return wrapper.doIndirectCall(evaluator, thisObject, functionName, arguments); + } + + /** + * + * + * @return ... + */ + public Enumeration getAllProperties() { + return wrapper.getProperties(); + } + + /** + * + * + * @return ... + */ + public Enumeration getProperties() { + return wrapper.getProperties(); + } + + /** + * + * + * @return ... + */ + public Object toJavaObject() { + return wrappedObject; + } + + /** + * An ESNode equals another object if it is an ESNode that wraps the same INode * or the wrapped INode itself. FIXME: doesen't check dbmapping/type! */ - public boolean equals (Object what) { - if (what == null) + public boolean equals(Object what) { + if (what == null) { return false; - if (what == this) + } + + if (what == this) { return true; + } + if (what instanceof ESGenericObject) { ESGenericObject other = (ESGenericObject) what; - return (wrappedObject.equals (other.wrappedObject)); + + return (wrappedObject.equals(other.wrappedObject)); } + return false; - } - + } } - - diff --git a/src/helma/scripting/fesi/ESMapWrapper.java b/src/helma/scripting/fesi/ESMapWrapper.java index e2f1b96d..ec7c6967 100644 --- a/src/helma/scripting/fesi/ESMapWrapper.java +++ b/src/helma/scripting/fesi/ESMapWrapper.java @@ -1,171 +1,189 @@ -// ESMapWrapper.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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.scripting.fesi; -import helma.framework.core.*; -import helma.objectmodel.INode; import FESI.Data.*; import FESI.Exceptions.*; import FESI.Interpreter.Evaluator; +import helma.framework.core.*; +import helma.objectmodel.INode; import java.util.*; - - /** - * An EcmaScript object that makes stuff in a hashtable accessible as its properties - */ - + * An EcmaScript object that makes stuff in a hashtable accessible as its properties + */ public class ESMapWrapper extends ESWrapper { - private Map data; private FesiEngine engine; private boolean readonly = false; - public ESMapWrapper (FesiEngine engine) { - super (new Object(), engine.getEvaluator ()); - this.engine = engine; + /** + * Creates a new ESMapWrapper object. + * + * @param engine ... + */ + public ESMapWrapper(FesiEngine engine) { + super(new Object(), engine.getEvaluator()); + this.engine = engine; } - public ESMapWrapper (FesiEngine engine, Map data) { - super (new Object(), engine.getEvaluator ()); - this.engine = engine; - this.data = data; + /** + * Creates a new ESMapWrapper object. + * + * @param engine ... + * @param data ... + */ + public ESMapWrapper(FesiEngine engine, Map data) { + super(new Object(), engine.getEvaluator()); + this.engine = engine; + this.data = data; } - public void setReadonly (boolean readonly) { - this.readonly = readonly; - } + /** + * + * + * @param readonly ... + */ + public void setReadonly(boolean readonly) { + this.readonly = readonly; + } - public void setData (Map data) { - this.data = data; + /** + * + * + * @param data ... + */ + public void setData(Map data) { + this.data = data; } /** * Overridden to make the object read-only */ - public void putProperty(String propertyName, ESValue propertyValue, int hash) throws EcmaScriptException { - if (data == null) - data = new HashMap (); - if (propertyValue == ESNull.theNull) - deleteProperty(propertyName, hash); - else if (readonly==false) - data.put (propertyName, propertyValue.toJavaObject ()); - else - throw new EcmaScriptException ("object is readonly"); - } - - public boolean deleteProperty(String propertyName, int hash) throws EcmaScriptException { - if (readonly==false) { - data.remove (propertyName); - return true; - } else - return false; - } - - public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException { - if (data == null) - return ESNull.theNull; + public void putProperty(String propertyName, ESValue propertyValue, int hash) + throws EcmaScriptException { + if (data == null) { + data = new HashMap(); + } - Object val = data.get (propertyName); - - if (val == null) - return ESNull.theNull; - - if (val instanceof String) - return new ESString ((String) val); - else if (val instanceof INode) - return engine.getNodeWrapper ((INode) val); - else if (val instanceof Map) - return new ESMapWrapper (engine, (Map)val); - else if (val instanceof ESValue) - return (ESValue) val; - return ESLoader.normalizeValue(val, evaluator); + if (propertyValue == ESNull.theNull) { + deleteProperty(propertyName, hash); + } else if (readonly == false) { + data.put(propertyName, propertyValue.toJavaObject()); + } else { + throw new EcmaScriptException("object is readonly"); + } } + /** + * + * + * @param propertyName ... + * @param hash ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public boolean deleteProperty(String propertyName, int hash) + throws EcmaScriptException { + if (readonly == false) { + data.remove(propertyName); - public Enumeration getAllProperties () { - return getProperties (); + return true; + } else { + return false; + } } - public Enumeration getProperties () { - Object[] keys = data == null ? null : data.keySet().toArray (); - return new Enum (keys); + /** + * + * + * @param propertyName ... + * @param hash ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public ESValue getProperty(String propertyName, int hash) + throws EcmaScriptException { + if (data == null) { + return ESNull.theNull; + } + + Object val = data.get(propertyName); + + if (val == null) { + return ESNull.theNull; + } + + if (val instanceof String) { + return new ESString((String) val); + } else if (val instanceof INode) { + return engine.getNodeWrapper((INode) val); + } else if (val instanceof Map) { + return new ESMapWrapper(engine, (Map) val); + } else if (val instanceof ESValue) { + return (ESValue) val; + } + + return ESLoader.normalizeValue(val, evaluator); } + /** + * + * + * @return ... + */ + public Enumeration getAllProperties() { + return getProperties(); + } + + /** + * + * + * @return ... + */ + public Enumeration getProperties() { + Object[] keys = (data == null) ? null : data.keySet().toArray(); + + return new Enum(keys); + } class Enum implements Enumeration { - - Object[] elements; - int pos; - - Enum (Object[] elements) { - this.elements = elements; - pos = 0; - } - - public boolean hasMoreElements () { - return elements != null && pos < elements.length; - } - - public Object nextElement () { - if (elements == null || pos >= elements.length) - throw new NoSuchElementException (); - return elements[pos++]; - } + Object[] elements; + int pos; + + Enum(Object[] elements) { + this.elements = elements; + pos = 0; + } + + public boolean hasMoreElements() { + return (elements != null) && (pos < elements.length); + } + + public Object nextElement() { + if ((elements == null) || (pos >= elements.length)) { + throw new NoSuchElementException(); + } + + return elements[pos++]; + } } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/scripting/fesi/ESNode.java b/src/helma/scripting/fesi/ESNode.java index 71dcf062..130b1e4d 100644 --- a/src/helma/scripting/fesi/ESNode.java +++ b/src/helma/scripting/fesi/ESNode.java @@ -1,33 +1,43 @@ -// ESNode.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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.scripting.fesi; +import FESI.Data.*; +import FESI.Exceptions.*; +import FESI.Interpreter.*; +import helma.framework.core.*; import helma.objectmodel.*; import helma.objectmodel.db.*; -import helma.framework.core.*; import helma.util.*; -import FESI.Interpreter.*; -import FESI.Exceptions.*; -import FESI.Data.*; import java.io.*; import java.util.*; - /** - * An EcmaScript wrapper around a Node object. This is the basic - * HopObject that can be stored in the internal or external databases. - */ - + * An EcmaScript wrapper around a Node object. This is the basic + * HopObject that can be stored in the internal or external databases. + */ public class ESNode extends ObjectPrototype { - // the INode object wrapped by this ESObject INode node; // the cache node - persistent nodes have a transient property as a commodity to // store temporary stuff. INode cache; + // the ecmascript wrapper for the cache. ESNode cacheWrapper; @@ -40,308 +50,485 @@ public class ESNode extends ObjectPrototype { /** * Constructor used to create transient cache nodes */ - public ESNode (INode node, FesiEngine engine) { - super (engine.getPrototype("hopobject"), engine.getEvaluator()); - this.engine = engine; - this.node = node; - cache = null; - cacheWrapper = null; + public ESNode(INode node, FesiEngine engine) { + super(engine.getPrototype("hopobject"), engine.getEvaluator()); + this.engine = engine; + this.node = node; + cache = null; + cacheWrapper = null; - // this is a transient node, set node handle to null - handle = null; + // this is a transient node, set node handle to null + handle = null; } - public ESNode (ESObject prototype, Evaluator evaluator, Object obj, FesiEngine engine) { - super (prototype, evaluator); - // eval.app.logEvent ("in ESNode constructor: "+o.getClass ()); - this.engine = engine; - if (obj == null) - node = new TransientNode (null); - else if (obj instanceof ESWrapper) - node = (INode) ((ESWrapper) obj).getJavaObject (); - else if (obj instanceof INode) - node = (INode) obj; - else - node = new TransientNode (obj.toString ()); - // set node handle to wrapped node - if (node instanceof Node) - handle = ((Node) node).getHandle (); - else - handle = null; + /** + * Creates a new ESNode object. + * + * @param prototype ... + * @param evaluator ... + * @param obj ... + * @param engine ... + */ + public ESNode(ESObject prototype, Evaluator evaluator, Object obj, FesiEngine engine) { + super(prototype, evaluator); - // cache Node is initialized on demend when it is needed - cache = null; - cacheWrapper = null; + // eval.app.logEvent ("in ESNode constructor: "+o.getClass ()); + this.engine = engine; + + if (obj == null) { + node = new TransientNode(null); + } else if (obj instanceof ESWrapper) { + node = (INode) ((ESWrapper) obj).getJavaObject(); + } else if (obj instanceof INode) { + node = (INode) obj; + } else { + node = new TransientNode(obj.toString()); + } + + // set node handle to wrapped node + if (node instanceof Node) { + handle = ((Node) node).getHandle(); + } else { + handle = null; + } + + // cache Node is initialized on demend when it is needed + cache = null; + cacheWrapper = null; } /** * Check if the node has been invalidated. If so, it has to be re-fetched * from the db via the app's node manager. */ - protected void checkNode () { - if (node.getState () == INode.INVALID) try { - node = handle.getNode (engine.app.getWrappedNodeManager ()); - } catch (Exception nx) {} - } - - public INode getNode () { - checkNode (); - return node; - } - - public void setPrototype (String protoName) { - checkNode (); - node.setPrototype (protoName); - } - - public String getPrototypeName () { - return node.getPrototype (); - } - - public String getESClassName () { - return "HopObject"; - } - - public String toString () { - if (node == null) - return ""; - return node.toString (); - } - - public String toDetailString () { - return "ES:[Object: builtin " + this.getClass().getName() + ":" + - ((node == null) ? "null" : node.toString()) + "]"; - } - - protected void setError (Throwable e) { - lastError = e; - } - - - public boolean add (ESValue what[]) { - checkNode (); - for (int i=0; i"; + } + + return node.toString(); + } + + /** + * + * + * @return ... + */ + public String toDetailString() { + return "ES:[Object: builtin " + this.getClass().getName() + ":" + + ((node == null) ? "null" : node.toString()) + "]"; + } + + protected void setError(Throwable e) { + lastError = e; + } + + /** + * + * + * @param what ... + * + * @return ... + */ + public boolean add(ESValue[] what) { + checkNode(); + + for (int i = 0; i < what.length; i++) + if (what[i] instanceof ESNode) { + ESNode esn = (ESNode) what[i]; + INode added = node.addNode(esn.getNode()); + } + return true; } - public ESValue list () { - checkNode (); - int l = node.numberOfNodes (); - Enumeration e = node.getSubnodes (); - ESObject ap = evaluator.getArrayPrototype(); - ArrayPrototype theArray = new ArrayPrototype(ap, evaluator); - if (e != null) { - theArray.setSize(l); - for (int i = 0; i 2) + } + + if ((pval == null) || (pval.length < 1) || (pval.length > 2)) { return false; - if (!(pval[0] instanceof ESNode)) + } + + if (!(pval[0] instanceof ESNode)) { return false; + } + ESNode esn = (ESNode) pval[0]; - INode pn = esn.getNode (); - if (!(pn instanceof helma.objectmodel.db.Node)) + INode pn = esn.getNode(); + + if (!(pn instanceof helma.objectmodel.db.Node)) { return false; + } + // check if there is an additional string element - if so, it's the property name by which the node is // accessed, otherwise it will be accessed as anonymous subnode via its id String propname = null; - if (pval.length == 2 && pval[1] != null && !(pval[1] instanceof ESNull)) - propname = pval[1].toString (); + + if ((pval.length == 2) && (pval[1] != null) && !(pval[1] instanceof ESNull)) { + propname = pval[1].toString(); + } + helma.objectmodel.db.Node n = (helma.objectmodel.db.Node) node; - n.setParent ((helma.objectmodel.db.Node) pn, propname); + + n.setParent((helma.objectmodel.db.Node) pn, propname); + return true; } + /** + * + * + * @param propertyName ... + * @param propertyValue ... + * @param hash ... + * + * @throws EcmaScriptException ... + */ + public void putProperty(String propertyName, ESValue propertyValue, int hash) + throws EcmaScriptException { + checkNode(); - public void putProperty(String propertyName, ESValue propertyValue, int hash) throws EcmaScriptException { - checkNode (); - // engine.app.logEvent ("put property called: "+propertyName+", "+propertyValue.getClass()); - if ("cache".equalsIgnoreCase (propertyName)) - throw new EcmaScriptException ("Can't modify read-only property \""+propertyName+"\"."); + // engine.app.logEvent ("put property called: "+propertyName+", "+propertyValue.getClass()); + if ("cache".equalsIgnoreCase(propertyName)) { + throw new EcmaScriptException("Can't modify read-only property \"" + + propertyName + "\"."); + } - if ("subnodeRelation".equalsIgnoreCase (propertyName)) { - node.setSubnodeRelation (propertyValue instanceof ESNull ? null : propertyValue.toString ()); - return; - } + if ("subnodeRelation".equalsIgnoreCase(propertyName)) { + node.setSubnodeRelation((propertyValue instanceof ESNull) ? null + : propertyValue.toString()); - // check if the property is write protected, i.e. the type.property file describes it as [readonly] - DbMapping dbm = node.getDbMapping (); - if (dbm != null) { - Relation rel = dbm.getPropertyRelation (propertyName); - if (rel != null && rel.isReadonly ()) - return; - } + return; + } - if (propertyValue instanceof ESNull || propertyValue instanceof ESUndefined) - node.unset (propertyName); - else if (propertyValue instanceof ESString) - node.setString (propertyName, propertyValue.toString ()); - else if (propertyValue instanceof ESBoolean) - node.setBoolean (propertyName, propertyValue.booleanValue ()); - else if (propertyValue instanceof ESNumber) - node.setFloat (propertyName, propertyValue.doubleValue ()); - else if (propertyValue instanceof DatePrototype) - node.setDate (propertyName, (Date) propertyValue.toJavaObject ()); - else if (propertyValue instanceof ESNode) { - // long now = System.currentTimeMillis (); - ESNode esn = (ESNode) propertyValue; - node.setNode (propertyName, esn.getNode ()); - // engine.app.logEvent ("*** spent "+(System.currentTimeMillis () - now)+" ms to set property "+propertyName); - } else { - // engine.app.logEvent ("got "+propertyValue.getClass ()); - // A persistent node can't store anything other than the types above, so throw an exception - // throw new EcmaScriptException ("Can't set a JavaScript Object or Array as property of "+node); - node.setJavaObject (propertyName, propertyValue.toJavaObject ()); - } - } - - public boolean deleteProperty(String propertyName, int hash) throws EcmaScriptException { - checkNode (); - // engine.app.logEvent ("delete property called: "+propertyName); - if (node.get (propertyName) != null) { - node.unset (propertyName); - return true; - } - return super.deleteProperty (propertyName, hash); - } - - public ESValue getProperty (int i) throws EcmaScriptException { - checkNode (); - INode n = node.getSubnodeAt (i); - if (n == null) - return ESNull.theNull; - return engine.getNodeWrapper (n); - } + // check if the property is write protected, i.e. the type.property file describes it as [readonly] + DbMapping dbm = node.getDbMapping(); - public void putProperty(int index, ESValue propertyValue) throws EcmaScriptException { - checkNode (); - if (propertyValue instanceof ESNode) { - ESNode n = (ESNode) propertyValue; - node.addNode (n.getNode (), index); - } else - throw new EcmaScriptException ("Can only add Nodes to Node arrays"); + if (dbm != null) { + Relation rel = dbm.getPropertyRelation(propertyName); + + if ((rel != null) && rel.isReadonly()) { + return; + } + } + + if (propertyValue instanceof ESNull || propertyValue instanceof ESUndefined) { + node.unset(propertyName); + } else if (propertyValue instanceof ESString) { + node.setString(propertyName, propertyValue.toString()); + } else if (propertyValue instanceof ESBoolean) { + node.setBoolean(propertyName, propertyValue.booleanValue()); + } else if (propertyValue instanceof ESNumber) { + node.setFloat(propertyName, propertyValue.doubleValue()); + } else if (propertyValue instanceof DatePrototype) { + node.setDate(propertyName, (Date) propertyValue.toJavaObject()); + } else if (propertyValue instanceof ESNode) { + // long now = System.currentTimeMillis (); + ESNode esn = (ESNode) propertyValue; + + node.setNode(propertyName, esn.getNode()); + + // engine.app.logEvent ("*** spent "+(System.currentTimeMillis () - now)+" ms to set property "+propertyName); + } else { + // engine.app.logEvent ("got "+propertyValue.getClass ()); + // A persistent node can't store anything other than the types above, so throw an exception + // throw new EcmaScriptException ("Can't set a JavaScript Object or Array as property of "+node); + node.setJavaObject(propertyName, propertyValue.toJavaObject()); + } } + /** + * + * + * @param propertyName ... + * @param hash ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public boolean deleteProperty(String propertyName, int hash) + throws EcmaScriptException { + checkNode(); + + // engine.app.logEvent ("delete property called: "+propertyName); + if (node.get(propertyName) != null) { + node.unset(propertyName); + + return true; + } + + return super.deleteProperty(propertyName, hash); + } + + /** + * + * + * @param i ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public ESValue getProperty(int i) throws EcmaScriptException { + checkNode(); + + INode n = node.getSubnodeAt(i); + + if (n == null) { + return ESNull.theNull; + } + + return engine.getNodeWrapper(n); + } + + /** + * + * + * @param index ... + * @param propertyValue ... + * + * @throws EcmaScriptException ... + */ + public void putProperty(int index, ESValue propertyValue) + throws EcmaScriptException { + checkNode(); + + if (propertyValue instanceof ESNode) { + ESNode n = (ESNode) propertyValue; + + node.addNode(n.getNode(), index); + } else { + throw new EcmaScriptException("Can only add Nodes to Node arrays"); + } + } /** * Retrieve a property from the node object or the underlying EcmaScript prototype. @@ -351,13 +538,18 @@ public class ESNode extends ObjectPrototype { * because generally things are divided cleanly between prototype and object - the * first holds the functions, the latter the mapped data properties. */ - public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException { - checkNode (); - // engine.app.logEvent ("get property called: "+propertyName); - ESValue retval = super.getProperty (propertyName, hash); - if (! (retval instanceof ESUndefined)) - return retval; - return getNodeProperty (propertyName); + public ESValue getProperty(String propertyName, int hash) + throws EcmaScriptException { + checkNode(); + + // engine.app.logEvent ("get property called: "+propertyName); + ESValue retval = super.getProperty(propertyName, hash); + + if (!(retval instanceof ESUndefined)) { + return retval; + } + + return getNodeProperty(propertyName); } /** @@ -365,164 +557,256 @@ public class ESNode extends ObjectPrototype { * This is called directly when we call get(x) on a hopobject, since we don't want to return * the prototype functions in that case. */ - public ESValue getNodeProperty (String propertyName) throws EcmaScriptException { - // check for null - if (propertyName == null) - return ESNull.theNull; - // persistent or persistent capable nodes have a cache property that's a transient node. - // it it hasn't requested before, initialize it now - if ("cache".equalsIgnoreCase (propertyName) && node instanceof Node) { - cache = node.getCacheNode (); - if (cacheWrapper == null) - cacheWrapper = new ESNode (cache, engine); - else - cacheWrapper.node = cache; - return cacheWrapper; - } - if ("subnodeRelation".equalsIgnoreCase (propertyName)) { - String rel = node.getSubnodeRelation (); - return rel == null ? (ESValue) ESNull.theNull : new ESString (rel); - } + public ESValue getNodeProperty(String propertyName) + throws EcmaScriptException { + // check for null + if (propertyName == null) { + return ESNull.theNull; + } - // Everything starting with an underscore is interpreted as internal property - if (propertyName.startsWith ("_")) - return getInternalProperty (propertyName); + // persistent or persistent capable nodes have a cache property that's a transient node. + // it it hasn't requested before, initialize it now + if ("cache".equalsIgnoreCase(propertyName) && node instanceof Node) { + cache = node.getCacheNode(); - // this _may_ do a relational query if properties are mapped to a relational type. - IProperty p = node.get (propertyName); - if (p != null) { - if (p.getType () == IProperty.STRING) { - String str = p.getStringValue (); - if (str == null) - return ESNull.theNull; - else - return new ESString (str); - } - if (p.getType () == IProperty.BOOLEAN) - return ESBoolean.makeBoolean (p.getBooleanValue ()); - if (p.getType () == IProperty.DATE) - return new DatePrototype (evaluator, p.getDateValue ()); - if (p.getType () == IProperty.INTEGER) - return new ESNumber ((double) p.getIntegerValue ()); - if (p.getType () == IProperty.FLOAT) - return new ESNumber (p.getFloatValue ()); - if (p.getType () == IProperty.NODE) { - INode nd = p.getNodeValue (); - if (nd == null) - return ESNull.theNull; - else - return engine.getNodeWrapper (nd); - } - if (p.getType () == IProperty.JAVAOBJECT) - return ESLoader.normalizeObject (p.getJavaObjectValue (), evaluator); - } + if (cacheWrapper == null) { + cacheWrapper = new ESNode(cache, engine); + } else { + cacheWrapper.node = cache; + } - // as last resort, try to get property as anonymous subnode - INode anon = (INode) node.getChildElement (propertyName); - if (anon != null) - return engine.getNodeWrapper (anon); + return cacheWrapper; + } - return ESNull.theNull; + if ("subnodeRelation".equalsIgnoreCase(propertyName)) { + String rel = node.getSubnodeRelation(); + + return (rel == null) ? (ESValue) ESNull.theNull : new ESString(rel); + } + + // Everything starting with an underscore is interpreted as internal property + if (propertyName.startsWith("_")) { + return getInternalProperty(propertyName); + } + + // this _may_ do a relational query if properties are mapped to a relational type. + IProperty p = node.get(propertyName); + + if (p != null) { + if (p.getType() == IProperty.STRING) { + String str = p.getStringValue(); + + if (str == null) { + return ESNull.theNull; + } else { + return new ESString(str); + } + } + + if (p.getType() == IProperty.BOOLEAN) { + return ESBoolean.makeBoolean(p.getBooleanValue()); + } + + if (p.getType() == IProperty.DATE) { + return new DatePrototype(evaluator, p.getDateValue()); + } + + if (p.getType() == IProperty.INTEGER) { + return new ESNumber((double) p.getIntegerValue()); + } + + if (p.getType() == IProperty.FLOAT) { + return new ESNumber(p.getFloatValue()); + } + + if (p.getType() == IProperty.NODE) { + INode nd = p.getNodeValue(); + + if (nd == null) { + return ESNull.theNull; + } else { + return engine.getNodeWrapper(nd); + } + } + + if (p.getType() == IProperty.JAVAOBJECT) { + return ESLoader.normalizeObject(p.getJavaObjectValue(), evaluator); + } + } + + // as last resort, try to get property as anonymous subnode + INode anon = (INode) node.getChildElement(propertyName); + + if (anon != null) { + return engine.getNodeWrapper(anon); + } + + return ESNull.theNull; } /** * Some internal properties defined for every Node object. These are most commonly * used for debugging Helma applications. */ - private ESValue getInternalProperty (String propertyName) throws EcmaScriptException { - if ("__id__".equals (propertyName) || "_id".equals (propertyName)) { - return new ESString (node.getID ()); - } - if ("__prototype__".equals (propertyName) || "_prototype".equals (propertyName)) { - String p = node.getPrototype (); - if (p == null) - return ESNull.theNull; - else - return new ESString (node.getPrototype ()); - } - if ("__parent__".equals (propertyName) || "_parent".equals (propertyName)) { - INode n = node.getParent (); - if (n == null) - return ESNull.theNull; - else - return engine.getNodeWrapper (n); - } - // some more internal properties - if ("__name__".equals (propertyName)) - return new ESString (node.getName ()); - if ("__fullname__".equals (propertyName)) - return new ESString (node.getFullName ()); - if ("__hash__".equals (propertyName)) - return new ESString (""+node.hashCode ()); - if ("__node__".equals (propertyName)) - return new ESWrapper (node, evaluator); - if ("__created__".equalsIgnoreCase (propertyName)) - return new DatePrototype (evaluator, node.created ()); - if ("__lastmodified__".equalsIgnoreCase (propertyName)) - return new DatePrototype (evaluator, node.lastModified ()); + private ESValue getInternalProperty(String propertyName) + throws EcmaScriptException { + if ("__id__".equals(propertyName) || "_id".equals(propertyName)) { + return new ESString(node.getID()); + } - return ESNull.theNull; + if ("__prototype__".equals(propertyName) || "_prototype".equals(propertyName)) { + String p = node.getPrototype(); + + if (p == null) { + return ESNull.theNull; + } else { + return new ESString(node.getPrototype()); + } + } + + if ("__parent__".equals(propertyName) || "_parent".equals(propertyName)) { + INode n = node.getParent(); + + if (n == null) { + return ESNull.theNull; + } else { + return engine.getNodeWrapper(n); + } + } + + // some more internal properties + if ("__name__".equals(propertyName)) { + return new ESString(node.getName()); + } + + if ("__fullname__".equals(propertyName)) { + return new ESString(node.getFullName()); + } + + if ("__hash__".equals(propertyName)) { + return new ESString("" + node.hashCode()); + } + + if ("__node__".equals(propertyName)) { + return new ESWrapper(node, evaluator); + } + + if ("__created__".equalsIgnoreCase(propertyName)) { + return new DatePrototype(evaluator, node.created()); + } + + if ("__lastmodified__".equalsIgnoreCase(propertyName)) { + return new DatePrototype(evaluator, node.lastModified()); + } + + return ESNull.theNull; } - public boolean clearCache () { - checkNode (); - node.clearCacheNode (); - return true; + /** + * + * + * @return ... + */ + public boolean clearCache() { + checkNode(); + node.clearCacheNode(); + + return true; } - public Enumeration getAllProperties () { - return getProperties (); + /** + * + * + * @return ... + */ + public Enumeration getAllProperties() { + return getProperties(); } - public Enumeration getProperties () { - checkNode (); - return node.properties (); + /** + * + * + * @return ... + */ + public Enumeration getProperties() { + checkNode(); + + return node.properties(); } - + /** + * + * + * @return ... + */ public String error() { - if (lastError == null) { - return ""; - } else { - String exceptionName = lastError.getClass().getName(); - int l = exceptionName.lastIndexOf("."); - if (l>0) exceptionName = exceptionName.substring(l+1); - return exceptionName +": " + lastError.getMessage(); - } + if (lastError == null) { + return ""; + } else { + String exceptionName = lastError.getClass().getName(); + int l = exceptionName.lastIndexOf("."); + + if (l > 0) { + exceptionName = exceptionName.substring(l + 1); + } + + return exceptionName + ": " + lastError.getMessage(); + } } - + + /** + * + */ public void clearError() { lastError = null; } - public Object toJavaObject () { - return getNode (); + /** + * + * + * @return ... + */ + public Object toJavaObject() { + return getNode(); } /** * An ESNode equals another object if it is an ESNode that wraps the same INode * or the wrapped INode itself. FIXME: doesen't check dbmapping/type! */ - public boolean equals (Object what) { - if (what == null) + public boolean equals(Object what) { + if (what == null) { return false; - if (what == this) + } + + if (what == this) { return true; + } + if (what instanceof ESNode) { ESNode other = (ESNode) what; - if (handle != null) - return handle.equals (other.handle); - else + + if (handle != null) { + return handle.equals(other.handle); + } else { return (node == other.node); + } } + return false; } + /** + * + * + * @param hint ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ public ESValue getDefaultValue(int hint) throws EcmaScriptException { - return new ESString (this.toString()); + return new ESString(this.toString()); } - -} // class ESNode - - - +} + // class ESNode diff --git a/src/helma/scripting/fesi/FesiActionAdapter.java b/src/helma/scripting/fesi/FesiActionAdapter.java index dca3a3e3..6051a4be 100644 --- a/src/helma/scripting/fesi/FesiActionAdapter.java +++ b/src/helma/scripting/fesi/FesiActionAdapter.java @@ -1,88 +1,121 @@ -// ActionFile.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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.scripting.fesi; -import helma.scripting.*; -import java.util.Vector; -import java.util.Iterator; -import java.io.*; -import helma.framework.*; -import helma.framework.core.*; -import helma.util.Updatable; -import FESI.Data.*; -import FESI.Parser.*; import FESI.AST.ASTFormalParameterList; import FESI.AST.ASTStatementList; import FESI.AST.EcmaScriptTreeConstants; -import FESI.Interpreter.*; +import FESI.Data.*; import FESI.Exceptions.*; - - +import FESI.Interpreter.*; +import FESI.Parser.*; +import helma.framework.*; +import helma.framework.core.*; +import helma.scripting.*; +import helma.util.Updatable; +import java.io.*; +import java.util.Iterator; +import java.util.Vector; /** * An class that updates fesi interpreters with actionfiles and templates. */ - - public class FesiActionAdapter { - Prototype prototype; Application app; String functionName; String sourceName; + // this is the parsed function which can be easily applied to FesiEngine objects - TypeUpdater pfunc, pfuncAsString; + TypeUpdater pfunc; - public FesiActionAdapter (ActionFile action) { - prototype = action.getPrototype (); - app = action.getApplication (); - Reader reader = null; - functionName = action.getFunctionName (); - sourceName = action.getSourceName (); - try { - reader = action.getReader (); - pfunc = parseFunction (functionName, - "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", - reader, - sourceName); - } catch (Throwable x) { - String message = x.getMessage (); - pfunc = new ErrorFeedback (functionName, message); - } finally { - try { - reader.close(); - } catch (Exception ignore) {} - } - // check if this is a template and we need to generate an "_as_string" variant - if (action instanceof Template) { - String content = "res.pushStringBuffer(); " + - action.getContent () + - "\r\nreturn res.popStringBuffer();\r\n"; - try { - pfuncAsString = parseFunction (functionName+"_as_string", - "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", - new StringReader(content), - sourceName); - } catch (Throwable x) { - String message = x.getMessage (); - pfunc = new ErrorFeedback (functionName+"_as_string", message); - } - } else { - pfuncAsString = null; - } + // this is the parsed function which can be easily applied to FesiEngine objects + TypeUpdater pfuncAsString; + + /** + * Creates a new FesiActionAdapter object. + * + * @param action ... + */ + public FesiActionAdapter(ActionFile action) { + prototype = action.getPrototype(); + app = action.getApplication(); + + Reader reader = null; + + functionName = action.getFunctionName(); + sourceName = action.getSourceName(); + + try { + reader = action.getReader(); + pfunc = parseFunction(functionName, + "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", + reader, sourceName); + } catch (Throwable x) { + String message = x.getMessage(); + + pfunc = new ErrorFeedback(functionName, message); + } finally { + try { + reader.close(); + } catch (Exception ignore) { + } + } + + // check if this is a template and we need to generate an "_as_string" variant + if (action instanceof Template) { + String content = "res.pushStringBuffer(); " + action.getContent() + + "\r\nreturn res.popStringBuffer();\r\n"; + + try { + pfuncAsString = parseFunction(functionName + "_as_string", + "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", + new StringReader(content), sourceName); + } catch (Throwable x) { + String message = x.getMessage(); + + pfunc = new ErrorFeedback(functionName + "_as_string", message); + } + } else { + pfuncAsString = null; + } } + /** + * + * + * @param engine ... + * + * @throws EcmaScriptException ... + */ + public synchronized void updateEvaluator(FesiEngine engine) + throws EcmaScriptException { + if (pfunc != null) { + pfunc.updateEvaluator(engine); + } - public synchronized void updateEvaluator (FesiEngine engine) throws EcmaScriptException { - if (pfunc != null) - pfunc.updateEvaluator (engine); - if (pfuncAsString != null) - pfuncAsString.updateEvaluator (engine); + if (pfuncAsString != null) { + pfuncAsString.updateEvaluator(engine); + } } - protected TypeUpdater parseFunction (String funcName, String params, Reader body, String sourceName) throws EcmaScriptException { - + protected TypeUpdater parseFunction(String funcName, String params, Reader body, + String sourceName) + throws EcmaScriptException { // ESObject fp = app.eval.evaluator.getFunctionPrototype(); // ConstructedFunctionObject function = null; ASTFormalParameterList fpl = null; @@ -90,116 +123,138 @@ public class FesiActionAdapter { FunctionEvaluationSource fes = null; /* if (body == null || "".equals (body.trim())) - body = ";\r\n"; - else - body = body + "\r\n"; */ - if (params == null) params = ""; - else params = params.trim (); + body = ";\r\n"; + else + body = body + "\r\n"; */ + if (params == null) { + params = ""; + } else { + params = params.trim(); + } EcmaScript parser; StringReader is; // Special case for empty parameters - if (params.length()==0) { + if (params.length() == 0) { fpl = new ASTFormalParameterList(EcmaScriptTreeConstants.JJTFORMALPARAMETERLIST); } else { is = new java.io.StringReader(params); parser = new EcmaScript(is); + try { fpl = (ASTFormalParameterList) parser.FormalParameterList(); is.close(); } catch (ParseException x) { - throw new EcmaScriptParseException (x, new FileEvaluationSource(sourceName, null)); + throw new EcmaScriptParseException(x, + new FileEvaluationSource(sourceName, + null)); } } + // this is very very very strange: without the toString, lots of obscure exceptions // deep inside the parser... // is = new java.io.StringReader(body.toString ()); try { - parser = new EcmaScript (body); + parser = new EcmaScript(body); sl = (ASTStatementList) parser.StatementList(); body.close(); } catch (ParseException x) { - app.logEvent ("Error parsing file "+app.getName()+":"+sourceName+": "+x); - throw new EcmaScriptParseException (x, new FileEvaluationSource(sourceName, null)); + app.logEvent("Error parsing file " + app.getName() + ":" + sourceName + ": " + + x); + throw new EcmaScriptParseException(x, + new FileEvaluationSource(sourceName, null)); } catch (Exception x) { - app.logEvent ("Error parsing file "+app.getName()+":"+sourceName+": "+x); - throw new RuntimeException (x.getMessage ()); + app.logEvent("Error parsing file " + app.getName() + ":" + sourceName + ": " + + x); + throw new RuntimeException(x.getMessage()); } - fes = new FunctionEvaluationSource (new FileEvaluationSource(sourceName, null), funcName); - return new ParsedFunction (fpl, sl, fes, null, funcName); + fes = new FunctionEvaluationSource(new FileEvaluationSource(sourceName, null), + funcName); + + return new ParsedFunction(fpl, sl, fes, null, funcName); + } + + interface TypeUpdater { + public void updateEvaluator(FesiEngine engine) + throws EcmaScriptException; } class ParsedFunction implements TypeUpdater { - ASTFormalParameterList fpl = null; ASTStatementList sl = null; FunctionEvaluationSource fes = null; String fullFunctionText = null; String functionName; - public ParsedFunction (ASTFormalParameterList fpl, ASTStatementList sl, FunctionEvaluationSource fes, - String fullFunctionText, String functionName) { - this.fpl = fpl; - this.sl = sl; - this.fes = fes; - this.fullFunctionText = fullFunctionText; - this.functionName = functionName; + public ParsedFunction(ASTFormalParameterList fpl, ASTStatementList sl, + FunctionEvaluationSource fes, String fullFunctionText, + String functionName) { + this.fpl = fpl; + this.sl = sl; + this.fes = fes; + this.fullFunctionText = fullFunctionText; + this.functionName = functionName; } - public void updateEvaluator (FesiEngine engine) throws EcmaScriptException { + public void updateEvaluator(FesiEngine engine) + throws EcmaScriptException { + ObjectPrototype op = engine.getPrototype(prototype.getName()); - ObjectPrototype op = engine.getPrototype (prototype.getName()); + EcmaScriptVariableVisitor vdvisitor = engine.evaluator.getVarDeclarationVisitor(); + Vector vnames = vdvisitor.processVariableDeclarations(sl, fes); - EcmaScriptVariableVisitor vdvisitor = engine.evaluator.getVarDeclarationVisitor(); - Vector vnames = vdvisitor.processVariableDeclarations(sl, fes); + FunctionPrototype fp = ConstructedFunctionObject.makeNewConstructedFunction(engine.evaluator, + functionName, + fes, + fullFunctionText, + fpl.getArguments(), + vnames, + sl); - FunctionPrototype fp = ConstructedFunctionObject.makeNewConstructedFunction ( - engine.evaluator, functionName, fes, - fullFunctionText, fpl.getArguments(), vnames, sl); - op.putHiddenProperty (functionName, fp); + op.putHiddenProperty(functionName, fp); } - } class ErrorFeedback implements TypeUpdater { + String functionName; + String errorMessage; - String functionName, errorMessage; - - public ErrorFeedback (String fname, String msg) { + public ErrorFeedback(String fname, String msg) { functionName = fname; errorMessage = msg; } - public void updateEvaluator (FesiEngine engine) throws EcmaScriptException { + public void updateEvaluator(FesiEngine engine) + throws EcmaScriptException { + ObjectPrototype op = engine.getPrototype(prototype.getName()); - ObjectPrototype op = engine.getPrototype (prototype.getName ()); - - FunctionPrototype fp = (FunctionPrototype) engine.evaluator.getFunctionPrototype (); - FunctionPrototype func = new ThrowException (functionName, engine.evaluator, fp, errorMessage); - op.putHiddenProperty (functionName, func); + FunctionPrototype fp = (FunctionPrototype) engine.evaluator.getFunctionPrototype(); + FunctionPrototype func = new ThrowException(functionName, engine.evaluator, + fp, errorMessage); + op.putHiddenProperty(functionName, func); } } class ThrowException extends BuiltinFunctionObject { String message; - ThrowException (String name, Evaluator evaluator, FunctionPrototype fp, String message) { - super (fp, evaluator, name, 1); - this.message = message == null ? "No error message available" : message; - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - throw new EcmaScriptException (message); - } - public ESObject doConstruct (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - throw new EcmaScriptException (message); - } - } - interface TypeUpdater { - public void updateEvaluator (FesiEngine engine) throws EcmaScriptException; + ThrowException(String name, Evaluator evaluator, FunctionPrototype fp, + String message) { + super(fp, evaluator, name, 1); + this.message = (message == null) ? "No error message available" : message; + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + throw new EcmaScriptException(message); + } + + public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + throw new EcmaScriptException(message); + } } } - - diff --git a/src/helma/scripting/fesi/FesiEngine.java b/src/helma/scripting/fesi/FesiEngine.java index 0cf96e79..f1fd7524 100644 --- a/src/helma/scripting/fesi/FesiEngine.java +++ b/src/helma/scripting/fesi/FesiEngine.java @@ -1,31 +1,55 @@ -// FesiScriptingEnvironment.java -// Copyright (c) Hannes Wallnöfer 2002 +/* + * 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.scripting.fesi; -import helma.scripting.*; -import helma.scripting.fesi.extensions.*; -import helma.extensions.HelmaExtension; +import FESI.Data.*; +import FESI.Exceptions.*; +import FESI.Interpreter.*; +import helma.doc.DocApplication; import helma.extensions.ConfigurationException; +import helma.extensions.HelmaExtension; import helma.framework.*; import helma.framework.core.*; -import helma.doc.DocApplication; +import helma.main.Server; import helma.objectmodel.*; import helma.objectmodel.db.DbMapping; import helma.objectmodel.db.Relation; -import helma.main.Server; -import helma.util.Updatable; +import helma.scripting.*; +import helma.scripting.fesi.extensions.*; import helma.util.CacheMap; -import java.util.*; +import helma.util.Updatable; import java.io.*; -import FESI.Data.*; -import FESI.Interpreter.*; -import FESI.Exceptions.*; +import java.util.*; /** * This is the implementation of ScriptingEnvironment for the FESI EcmaScript interpreter. */ public class FesiEngine implements ScriptingEngine { + // extensions loaded by this evaluator + static final String[] extensions = new String[] { + "FESI.Extensions.BasicIO", + "FESI.Extensions.FileIO", + "helma.scripting.fesi.extensions.XmlRpcExtension", + "helma.scripting.fesi.extensions.ImageExtension", + "helma.scripting.fesi.extensions.FtpExtension", + "FESI.Extensions.JavaAccess", + "helma.scripting.fesi.extensions.DomExtension", + "FESI.Extensions.OptionalRegExp" + }; // the application we're running in Application app; @@ -45,258 +69,310 @@ public class FesiEngine implements ScriptingEngine { // the request evaluator instance owning this fesi evaluator RequestEvaluator reval; - // extensions loaded by this evaluator - static final String[] extensions = new String[] { - "FESI.Extensions.BasicIO", - "FESI.Extensions.FileIO", - "helma.scripting.fesi.extensions.XmlRpcExtension", - "helma.scripting.fesi.extensions.ImageExtension", - "helma.scripting.fesi.extensions.FtpExtension", - "FESI.Extensions.JavaAccess", - "helma.scripting.fesi.extensions.DomExtension", - "FESI.Extensions.OptionalRegExp"}; - // remember global variables from last invokation to be able to // do lazy cleanup Map lastGlobals = null; // the global vars set by extensions HashMap extensionGlobals; - + // the introspector that provides documentation for this application DocApplication doc = null; /** * Zero argument constructor. */ - public FesiEngine () {} + public FesiEngine() { + } /** * Initialize a FESI evaluator for the given application and request evaluator. */ - public void init (Application app, RequestEvaluator reval) { - this.app = app; - this.reval = reval; - wrappercache = new CacheMap (200, .75f); - prototypes = new Hashtable (); - try { - evaluator = new Evaluator(this); - global = evaluator.getGlobalObject(); - for (int i=0; i 0) { - Prototype p = app.typemgr.getPrototype (info.protoName); - if (p != null) { - // System.err.println ("UPDATING PROTO: "+p); - app.typemgr.updatePrototype(p); - if (p.getLastUpdate () > info.lastUpdate) { - evaluatePrototype(p); - info.lastUpdate = p.getLastUpdate (); - } - } - } - } + public void updatePrototypes() { + // set the thread filed in the FESI evaluator + evaluator.thread = Thread.currentThread(); + + Collection protos = app.getPrototypes(); + + for (Iterator i = protos.iterator(); i.hasNext();) { + Prototype proto = (Prototype) i.next(); + TypeInfo info = (TypeInfo) prototypes.get(proto.getName()); + + if (info == null) { + // a prototype we don't know anything about yet. Init local update info. + initPrototype(proto); + info = (TypeInfo) prototypes.get(proto.getName()); + } + + // only update prototype if it has already been initialized. + // otherwise, this will be done on demand + // System.err.println ("CHECKING PROTO: "+info); + if (info.lastUpdate > 0) { + Prototype p = app.typemgr.getPrototype(info.protoName); + + if (p != null) { + // System.err.println ("UPDATING PROTO: "+p); + app.typemgr.updatePrototype(p); + + if (p.getLastUpdate() > info.lastUpdate) { + evaluatePrototype(p); + info.lastUpdate = p.getLastUpdate(); + } + } + } + } } /** @@ -304,138 +380,170 @@ public class FesiEngine implements ScriptingEngine { * evaluation is entered. The globals parameter contains the global values * to be applied during this execution context. */ - public void enterContext (HashMap globals) throws ScriptingException { - // set the thread filed in the FESI evaluator - evaluator.thread = Thread.currentThread (); - // set globals on the global object - globals.putAll(extensionGlobals); - if (globals != null && globals != lastGlobals) { - // loop through global vars and set them - for (Iterator i=globals.keySet().iterator(); i.hasNext(); ) { - String k = (String) i.next(); - Object v = globals.get (k); - ESValue sv = null; - try { - // we do a lot of extra work to make access to global variables - // comfortable to EcmaScript coders, i.e. we use a lot of custom wrappers - // that expose properties and functions in a special way instead of just going - // with the standard java object wrappers. - if (v instanceof Map) { - sv = new ESMapWrapper (this, (Map) v); - } else if ("path".equals (k)) { - ArrayPrototype parr = new ArrayPrototype (evaluator.getArrayPrototype(), evaluator); - List path = (List) v; - // register path elements with their prototype - for (int j=0; j info.lastUpdate) { - info.lastUpdate = p.getLastUpdate (); + + if (p.getLastUpdate() > info.lastUpdate) { + info.lastUpdate = p.getLastUpdate(); evaluatePrototype(p); } - // set info.lastUpdate to 1 if it is 0 so we know we - // have initialized this prototype already, even if - // it is empty (i.e. doesn't contain any scripts/skins/actoins - if (info.lastUpdate == 0) - info.lastUpdate = 1; + + // set info.lastUpdate to 1 if it is 0 so we know we + // have initialized this prototype already, even if + // it is empty (i.e. doesn't contain any scripts/skins/actoins + if (info.lastUpdate == 0) { + info.lastUpdate = 1; + } } } - return info == null? null : info.objectPrototype; + + return (info == null) ? null : info.objectPrototype; } /** * Register an object prototype for a certain prototype name. */ - public void putPrototype (String protoName, ObjectPrototype op) { - if (protoName != null && op != null) - prototypes.put (protoName, new TypeInfo (op, protoName)); + public void putPrototype(String protoName, ObjectPrototype op) { + if ((protoName != null) && (op != null)) { + prototypes.put(protoName, new TypeInfo(op, protoName)); + } } - /** * Get a Script wrapper for an object. In contrast to getElementWrapper, this is called for * any Java object, not just the ones in the request path which we know are scripted. * So what we do is check if the object belongs to a scripted class. If so, we call getElementWrapper() * with the object, otherwise we return a generic unscripted object wrapper. */ - public ESValue getObjectWrapper (Object e) { - if (app.getPrototypeName (e) != null) - return getElementWrapper (e); - /* else if (e instanceof Map) - return new ESMapWrapper (this, (Map) e); */ - /* else if (e instanceof INode) - return new ESNode ((INode) e, this); */ - else - return new ESWrapper (e, evaluator); + public ESValue getObjectWrapper(Object e) { + if (app.getPrototypeName(e) != null) { + return getElementWrapper(e); + } + /* else if (e instanceof Map) + return new ESMapWrapper (this, (Map) e); */ + /* else if (e instanceof INode) + return new ESNode ((INode) e, this); */ + else { + return new ESWrapper(e, evaluator); + } } /** @@ -692,36 +872,37 @@ public class FesiEngine implements ScriptingEngine { * interface, the getPrototype method will be used to retrieve the name of the prototype * to use. Otherwise, a Java-Class-to-Script-Prototype mapping is consulted. */ - public ESObject getElementWrapper (Object e) { + public ESObject getElementWrapper(Object e) { + // check if e is an instance of a helma objectmodel node. + if (e instanceof INode) { + return getNodeWrapper((INode) e); + } - // check if e is an instance of a helma objectmodel node. - if (e instanceof INode) - return getNodeWrapper ((INode) e); + // Gotta find out the prototype name to use for this object... + String prototypeName = app.getPrototypeName(e); - // Gotta find out the prototype name to use for this object... - String prototypeName = app.getPrototypeName (e); - - ObjectPrototype op = getPrototype (prototypeName); + ObjectPrototype op = getPrototype(prototypeName); - if (op == null) - op = getPrototype ("hopobject"); + if (op == null) { + op = getPrototype("hopobject"); + } - return new ESGenericObject (op, evaluator, e); + return new ESGenericObject(op, evaluator, e); } - - /** * Get a script wrapper for an instance of helma.objectmodel.INode */ - public ESNode getNodeWrapper (INode n) { + public ESNode getNodeWrapper(INode n) { // FIXME: should this return ESNull.theNull? - if (n == null) + if (n == null) { return null; + } - ESNode esn = (ESNode) wrappercache.get (n); - if (esn == null || esn.getNode() != n) { - String protoname = n.getPrototype (); + ESNode esn = (ESNode) wrappercache.get(n); + + if ((esn == null) || (esn.getNode() != n)) { + String protoname = n.getPrototype(); ObjectPrototype op = null; @@ -729,123 +910,129 @@ public class FesiEngine implements ScriptingEngine { // this *should* be done on the objectmodel level, but isn't currently // for embedded nodes since there's not enough type info at the objectmodel level // for those nodes. - if (protoname != null && protoname.length() > 0 && n.getDbMapping () == null) { - n.setDbMapping (app.getDbMapping (protoname)); + if ((protoname != null) && (protoname.length() > 0) && + (n.getDbMapping() == null)) { + n.setDbMapping(app.getDbMapping(protoname)); } - op = getPrototype (protoname); + op = getPrototype(protoname); // no prototype found for this node? - if (op == null) + if (op == null) { op = getPrototype("hopobject"); + } - esn = new ESNode (op, evaluator, n, this); + esn = new ESNode(op, evaluator, n, this); + + wrappercache.put(n, esn); - wrappercache.put (n, esn); // app.logEvent ("Wrapper for "+n+" created"); } return esn; } - /** * Register a new Node wrapper with the wrapper cache. This is used by the * Node constructor. */ - public void putNodeWrapper (INode n, ESNode esn) { - wrappercache.put (n, esn); + public void putNodeWrapper(INode n, ESNode esn) { + wrappercache.put(n, esn); } /** * Return the application's classloader */ - public ClassLoader getClassLoader () { - return app.getClassLoader (); + public ClassLoader getClassLoader() { + return app.getClassLoader(); } /** * Return the RequestEvaluator owning and driving this FESI evaluator. */ - public RequestEvaluator getRequestEvaluator () { - return reval; + public RequestEvaluator getRequestEvaluator() { + return reval; } /** * Return the Response object of the current evaluation context. Proxy method to RequestEvaluator. */ - public ResponseTrans getResponse () { - return reval.res; + public ResponseTrans getResponse() { + return reval.res; } /** * Return the Request object of the current evaluation context. Proxy method to RequestEvaluator. */ - public RequestTrans getRequest () { - return reval.req; + public RequestTrans getRequest() { + return reval.req; } - private synchronized void evaluate (Prototype prototype, Object code) { - if (code instanceof FunctionFile) { - FunctionFile funcfile = (FunctionFile) code; - File file = funcfile.getFile (); - if (file != null) { - try { - FileReader fr = new FileReader (file); - EvaluationSource es = new FileEvaluationSource (funcfile.getSourceName(), null); - updateEvaluator (prototype, fr, es); - } catch (IOException iox) { - app.logEvent ("Error updating function file: "+iox); - } - } else { - StringReader reader = new StringReader (funcfile.getContent()); - EvaluationSource es = new FileEvaluationSource (funcfile.getSourceName(), null); - updateEvaluator (prototype, reader, es); - } - } else if (code instanceof ActionFile) { - ActionFile action = (ActionFile) code; - FesiActionAdapter fa = new FesiActionAdapter (action); - try { - fa.updateEvaluator (this); - } catch (EcmaScriptException esx) { - app.logEvent ("Error parsing "+action+": "+esx); - } - } - } + private synchronized void evaluate(Prototype prototype, Object code) { + if (code instanceof FunctionFile) { + FunctionFile funcfile = (FunctionFile) code; + File file = funcfile.getFile(); - - private synchronized void updateEvaluator (Prototype prototype, Reader reader, EvaluationSource source) { - try { - ObjectPrototype op = getPrototype (prototype.getName()); - // do the update, evaluating the file - evaluator.evaluate(reader, op, source, false); - } catch (Throwable e) { - app.logEvent ("Error parsing function file "+source+": "+e); - } finally { - if (reader != null) { + if (file != null) { try { - reader.close(); - } catch (IOException ignore) {} + FileReader fr = new FileReader(file); + EvaluationSource es = new FileEvaluationSource(funcfile.getSourceName(), + null); + + updateEvaluator(prototype, fr, es); + } catch (IOException iox) { + app.logEvent("Error updating function file: " + iox); + } + } else { + StringReader reader = new StringReader(funcfile.getContent()); + EvaluationSource es = new FileEvaluationSource(funcfile.getSourceName(), + null); + + updateEvaluator(prototype, reader, es); + } + } else if (code instanceof ActionFile) { + ActionFile action = (ActionFile) code; + FesiActionAdapter fa = new FesiActionAdapter(action); + + try { + fa.updateEvaluator(this); + } catch (EcmaScriptException esx) { + app.logEvent("Error parsing " + action + ": " + esx); } } } + private synchronized void updateEvaluator(Prototype prototype, Reader reader, + EvaluationSource source) { + try { + ObjectPrototype op = getPrototype(prototype.getName()); + + // do the update, evaluating the file + evaluator.evaluate(reader, op, source, false); + } catch (Throwable e) { + app.logEvent("Error parsing function file " + source + ": " + e); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException ignore) { + } + } + } + } class TypeInfo { - ObjectPrototype objectPrototype; long lastUpdate = 0; String protoName; - public TypeInfo (ObjectPrototype op, String name) { + public TypeInfo(ObjectPrototype op, String name) { objectPrototype = op; protoName = name; } - public String toString () { - return ("TypeInfo["+protoName+","+new Date(lastUpdate)+"]"); + public String toString() { + return ("TypeInfo[" + protoName + "," + new Date(lastUpdate) + "]"); } - } - } diff --git a/src/helma/scripting/fesi/HopExtension.java b/src/helma/scripting/fesi/HopExtension.java index 5e241596..9b0ed567 100644 --- a/src/helma/scripting/fesi/HopExtension.java +++ b/src/helma/scripting/fesi/HopExtension.java @@ -1,370 +1,576 @@ -// HopExtension.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * 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.scripting.fesi; -import helma.framework.*; -import helma.framework.core.*; -import helma.objectmodel.*; -import helma.util.*; -import helma.framework.IPathElement; -import helma.scripting.ScriptingException; -import FESI.Interpreter.*; +import FESI.Data.*; import FESI.Exceptions.*; import FESI.Extensions.*; -import FESI.Data.*; +import FESI.Interpreter.*; +import helma.framework.*; +import helma.framework.IPathElement; +import helma.framework.core.*; +import helma.objectmodel.*; +import helma.scripting.ScriptingException; +import helma.util.*; +import org.xml.sax.InputSource; import java.io.*; -import java.util.*; -import java.text.*; +import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; -import java.net.HttpURLConnection; +import java.text.*; import java.text.SimpleDateFormat; -import org.xml.sax.InputSource; +import java.util.*; /** * This is the basic Extension for FESI interpreters used in Helma. It sets up * varios constructors, global functions and properties on the HopObject prototype * (Node objects), the global prototype, the session object etc. */ - public final class HopExtension { - protected Application app; protected FesiEngine engine; - public HopExtension (Application app) { + // previously in FESI.Data.NumberObject + private Hashtable formatTable = new Hashtable(); + + /** + * Creates a new HopExtension object. + * + * @param app ... + */ + public HopExtension(Application app) { this.app = app; } - /** * Called by the evaluator after the extension is loaded. */ - public void initializeExtension (FesiEngine engine) throws EcmaScriptException { + public void initializeExtension(FesiEngine engine) + throws EcmaScriptException { this.engine = engine; - Evaluator evaluator = engine.getEvaluator (); + + Evaluator evaluator = engine.getEvaluator(); GlobalObject go = evaluator.getGlobalObject(); FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); ESObject op = evaluator.getObjectPrototype(); // The editor functions for String, Boolean and Number are deprecated! - ESObject sp = evaluator.getStringPrototype (); - sp.putHiddenProperty ("editor", new StringEditor ("editor", evaluator, fp)); - ESObject np = evaluator.getNumberPrototype (); - np.putHiddenProperty ("editor", new NumberEditor ("editor", evaluator, fp)); - np.putHiddenProperty ("format", new NumberPrototypeFormat ("format", evaluator, fp)); - ESObject bp = evaluator.getBooleanPrototype (); - bp.putHiddenProperty ("editor", new BooleanEditor ("editor", evaluator, fp)); - ESObject dp = evaluator.getDatePrototype (); - dp.putHiddenProperty ("format", new DatePrototypeFormat ("format", evaluator, fp)); + ESObject sp = evaluator.getStringPrototype(); - sp.putHiddenProperty ("trim", new StringTrim ("trim", evaluator, fp)); + sp.putHiddenProperty("editor", new StringEditor("editor", evaluator, fp)); + + ESObject np = evaluator.getNumberPrototype(); + + np.putHiddenProperty("editor", new NumberEditor("editor", evaluator, fp)); + np.putHiddenProperty("format", new NumberPrototypeFormat("format", evaluator, fp)); + + ESObject bp = evaluator.getBooleanPrototype(); + + bp.putHiddenProperty("editor", new BooleanEditor("editor", evaluator, fp)); + + ESObject dp = evaluator.getDatePrototype(); + + dp.putHiddenProperty("format", new DatePrototypeFormat("format", evaluator, fp)); + + sp.putHiddenProperty("trim", new StringTrim("trim", evaluator, fp)); // generic (Java wrapper) object prototype - ObjectPrototype esObjectPrototype = new ObjectPrototype (op, evaluator); + ObjectPrototype esObjectPrototype = new ObjectPrototype(op, evaluator); + // the Node prototype ObjectPrototype esNodePrototype = new ObjectPrototype(op, evaluator); + // the Session prototype - ObjectPrototype esSessionPrototype = new ObjectPrototype (esNodePrototype, evaluator); + ObjectPrototype esSessionPrototype = new ObjectPrototype(esNodePrototype, + evaluator); + // the Node constructor - ESObject node = new NodeConstructor ("Node", fp, engine); + ESObject node = new NodeConstructor("Node", fp, engine); // register the default methods of Node objects in the Node prototype - esNodePrototype.putHiddenProperty ("add", new NodeAdd ("add", evaluator, fp)); - esNodePrototype.putHiddenProperty ("addAt", new NodeAddAt ("addAt", evaluator, fp)); - esNodePrototype.putHiddenProperty ("remove", new NodeRemove ("remove", evaluator, fp)); - esNodePrototype.putHiddenProperty ("link", new NodeLink ("link", evaluator, fp)); - esNodePrototype.putHiddenProperty ("list", new NodeList ("list", evaluator, fp)); - esNodePrototype.putHiddenProperty ("set", new NodeSet ("set", evaluator, fp)); - esNodePrototype.putHiddenProperty ("get", new NodeGet ("get", evaluator, fp)); - esNodePrototype.putHiddenProperty ("count", new NodeCount ("count", evaluator, fp)); - esNodePrototype.putHiddenProperty ("contains", new NodeContains ("contains", evaluator, fp)); - esNodePrototype.putHiddenProperty ("size", new NodeCount ("size", evaluator, fp)); - esNodePrototype.putHiddenProperty ("editor", new NodeEditor ("editor", evaluator, fp)); - esNodePrototype.putHiddenProperty ("path", new NodeHref ("path", evaluator, fp)); - esNodePrototype.putHiddenProperty ("href", new NodeHref ("href", evaluator, fp)); - esNodePrototype.putHiddenProperty ("setParent", new NodeSetParent ("setParent", evaluator, fp)); - esNodePrototype.putHiddenProperty ("invalidate", new NodeInvalidate ("invalidate", evaluator, fp)); - esNodePrototype.putHiddenProperty ("prefetchChildren", new NodePrefetch ("prefetchChildren", evaluator, fp)); - esNodePrototype.putHiddenProperty ("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, false, false)); - esNodePrototype.putHiddenProperty ("renderSkinAsString", new RenderSkin ("renderSkinAsString", evaluator, fp, false, true)); - esNodePrototype.putHiddenProperty ("clearCache", new NodeClearCache ("clearCache", evaluator, fp)); + esNodePrototype.putHiddenProperty("add", new NodeAdd("add", evaluator, fp)); + esNodePrototype.putHiddenProperty("addAt", new NodeAddAt("addAt", evaluator, fp)); + esNodePrototype.putHiddenProperty("remove", + new NodeRemove("remove", evaluator, fp)); + esNodePrototype.putHiddenProperty("link", new NodeLink("link", evaluator, fp)); + esNodePrototype.putHiddenProperty("list", new NodeList("list", evaluator, fp)); + esNodePrototype.putHiddenProperty("set", new NodeSet("set", evaluator, fp)); + esNodePrototype.putHiddenProperty("get", new NodeGet("get", evaluator, fp)); + esNodePrototype.putHiddenProperty("count", new NodeCount("count", evaluator, fp)); + esNodePrototype.putHiddenProperty("contains", + new NodeContains("contains", evaluator, fp)); + esNodePrototype.putHiddenProperty("size", new NodeCount("size", evaluator, fp)); + esNodePrototype.putHiddenProperty("editor", + new NodeEditor("editor", evaluator, fp)); + esNodePrototype.putHiddenProperty("path", new NodeHref("path", evaluator, fp)); + esNodePrototype.putHiddenProperty("href", new NodeHref("href", evaluator, fp)); + esNodePrototype.putHiddenProperty("setParent", + new NodeSetParent("setParent", evaluator, fp)); + esNodePrototype.putHiddenProperty("invalidate", + new NodeInvalidate("invalidate", evaluator, fp)); + esNodePrototype.putHiddenProperty("prefetchChildren", + new NodePrefetch("prefetchChildren", evaluator, + fp)); + esNodePrototype.putHiddenProperty("renderSkin", + new RenderSkin("renderSkin", evaluator, fp, + false, false)); + esNodePrototype.putHiddenProperty("renderSkinAsString", + new RenderSkin("renderSkinAsString", evaluator, + fp, false, true)); + esNodePrototype.putHiddenProperty("clearCache", + new NodeClearCache("clearCache", evaluator, fp)); // default methods for generic Java wrapper object prototype. // This is a small subset of the methods in esNodePrototype. - esObjectPrototype.putHiddenProperty ("href", new NodeHref ("href", evaluator, fp)); - esObjectPrototype.putHiddenProperty("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, false, false)); - esObjectPrototype.putHiddenProperty("renderSkinAsString", new RenderSkin ("renderSkinAsString", evaluator, fp, false, true)); + esObjectPrototype.putHiddenProperty("href", new NodeHref("href", evaluator, fp)); + esObjectPrototype.putHiddenProperty("renderSkin", + new RenderSkin("renderSkin", evaluator, fp, + false, false)); + esObjectPrototype.putHiddenProperty("renderSkinAsString", + new RenderSkin("renderSkinAsString", + evaluator, fp, false, true)); // methods that give access to properties and global user lists go.putHiddenProperty("Node", node); // register the constructor for a plain Node object. go.putHiddenProperty("HopObject", node); // HopObject is the new name for node. - go.putHiddenProperty("getProperty", new GlobalGetProperty ("getProperty", evaluator, fp)); - go.putHiddenProperty("token", new GlobalGetProperty ("token", evaluator, fp)); - go.putHiddenProperty("getAge", new GlobalGetAge ("getAge", evaluator, fp)); - go.putHiddenProperty("getURL", new GlobalGetURL ("getURL", evaluator, fp)); - go.putHiddenProperty("encode", new GlobalEncode ("encode", evaluator, fp)); - go.putHiddenProperty("encodeXml", new GlobalEncodeXml ("encodeXml", evaluator, fp)); - go.putHiddenProperty("encodeForm", new GlobalEncodeForm ("encodeForm", evaluator, fp)); - go.putHiddenProperty("format", new GlobalFormat ("format", evaluator, fp)); - go.putHiddenProperty("stripTags", new GlobalStripTags ("stripTags", evaluator, fp)); - go.putHiddenProperty("getXmlDocument", new GlobalGetXmlDocument ("getXmlDocument", evaluator, fp)); - go.putHiddenProperty("getHtmlDocument", new GlobalGetHtmlDocument ("getHtmlDocument", evaluator, fp)); - go.putHiddenProperty("jdomize", new GlobalJDOM ("jdomize", evaluator, fp)); - go.putHiddenProperty("getSkin", new GlobalCreateSkin ("getSkin", evaluator, fp)); - go.putHiddenProperty("createSkin", new GlobalCreateSkin ("createSkin", evaluator, fp)); - go.putHiddenProperty("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, true, false)); - go.putHiddenProperty("renderSkinAsString", new RenderSkin ("renderSkinAsString", evaluator, fp, true, true)); - go.putHiddenProperty("authenticate", new GlobalAuthenticate ("authenticate", evaluator, fp)); + go.putHiddenProperty("getProperty", + new GlobalGetProperty("getProperty", evaluator, fp)); + go.putHiddenProperty("token", new GlobalGetProperty("token", evaluator, fp)); + go.putHiddenProperty("getAge", new GlobalGetAge("getAge", evaluator, fp)); + go.putHiddenProperty("getURL", new GlobalGetURL("getURL", evaluator, fp)); + go.putHiddenProperty("encode", new GlobalEncode("encode", evaluator, fp)); + go.putHiddenProperty("encodeXml", new GlobalEncodeXml("encodeXml", evaluator, fp)); + go.putHiddenProperty("encodeForm", + new GlobalEncodeForm("encodeForm", evaluator, fp)); + go.putHiddenProperty("format", new GlobalFormat("format", evaluator, fp)); + go.putHiddenProperty("stripTags", new GlobalStripTags("stripTags", evaluator, fp)); + go.putHiddenProperty("getXmlDocument", + new GlobalGetXmlDocument("getXmlDocument", evaluator, fp)); + go.putHiddenProperty("getHtmlDocument", + new GlobalGetHtmlDocument("getHtmlDocument", evaluator, fp)); + go.putHiddenProperty("jdomize", new GlobalJDOM("jdomize", evaluator, fp)); + go.putHiddenProperty("getSkin", new GlobalCreateSkin("getSkin", evaluator, fp)); + go.putHiddenProperty("createSkin", + new GlobalCreateSkin("createSkin", evaluator, fp)); + go.putHiddenProperty("renderSkin", + new RenderSkin("renderSkin", evaluator, fp, true, false)); + go.putHiddenProperty("renderSkinAsString", + new RenderSkin("renderSkinAsString", evaluator, fp, true, + true)); + go.putHiddenProperty("authenticate", + new GlobalAuthenticate("authenticate", evaluator, fp)); go.deleteProperty("exit", "exit".hashCode()); // register object prototypes with FesiEngine - engine.putPrototype ("global", go); - engine.putPrototype ("hopobject", esNodePrototype); - engine.putPrototype ("__javaobject__", esObjectPrototype); -// engine.putPrototype ("session", esSessionPrototype); + engine.putPrototype("global", go); + engine.putPrototype("hopobject", esNodePrototype); + engine.putPrototype("__javaobject__", esObjectPrototype); + + // engine.putPrototype ("session", esSessionPrototype); + } + + private String getNumberChoice(String name, double from, double to, double step, + double value) { + double l = 0.000001; + StringBuffer b = new StringBuffer(""); + + return b.toString(); } class NodeAdd extends BuiltinFunctionObject { - NodeAdd (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NodeAdd(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.add (arguments)); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode node = (ESNode) thisObject; + + return ESBoolean.makeBoolean(node.add(arguments)); } } class NodeAddAt extends BuiltinFunctionObject { - NodeAddAt (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NodeAddAt(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.addAt (arguments)); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode node = (ESNode) thisObject; + + return ESBoolean.makeBoolean(node.addAt(arguments)); } } class NodeRemove extends BuiltinFunctionObject { - NodeRemove (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NodeRemove(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.remove (arguments)); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode node = (ESNode) thisObject; + + return ESBoolean.makeBoolean(node.remove(arguments)); } } - class NodeLink extends BuiltinFunctionObject { - NodeLink (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + 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)); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode node = (ESNode) thisObject; + + return ESBoolean.makeBoolean(node.link(arguments)); } } - + class NodeList extends BuiltinFunctionObject { - NodeList (String name, Evaluator evaluator, FunctionPrototype fp) { + NodeList(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 0); } - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESNode node = (ESNode) thisObject; - return node.list (); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode node = (ESNode) thisObject; + + return node.list(); } } class NodeGet extends BuiltinFunctionObject { - NodeGet (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NodeGet(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESValue esv = null; - if (arguments[0].isNumberValue ()) { - int i = arguments[0].toInt32 (); - esv = thisObject.getProperty (i); - } else { - String name = arguments[0].toString (); - // call esNodeProperty() method special to ESNode because we want to avoid - // retrieving prototype functions when calling hopobject.get(). - ESNode esn = (ESNode) thisObject; - esv = esn.getNodeProperty (name); - } - return (esv); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESValue esv = null; + + if (arguments[0].isNumberValue()) { + int i = arguments[0].toInt32(); + + esv = thisObject.getProperty(i); + } else { + String name = arguments[0].toString(); + + // call esNodeProperty() method special to ESNode because we want to avoid + // retrieving prototype functions when calling hopobject.get(). + ESNode esn = (ESNode) thisObject; + + esv = esn.getNodeProperty(name); + } + + return (esv); } } class NodeSet extends BuiltinFunctionObject { - NodeSet (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NodeSet(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESNode esn = (ESNode) thisObject; - if (arguments[0].isNumberValue ()) { - return ESBoolean.makeBoolean (esn.addAt (arguments)); - } else { - String propname = arguments[0].toString (); - esn.putProperty (propname, arguments[1], propname.hashCode ()); - } - return ESBoolean.makeBoolean (true); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode esn = (ESNode) thisObject; + + if (arguments[0].isNumberValue()) { + return ESBoolean.makeBoolean(esn.addAt(arguments)); + } else { + String propname = arguments[0].toString(); + + esn.putProperty(propname, arguments[1], propname.hashCode()); + } + + return ESBoolean.makeBoolean(true); } } class NodeCount extends BuiltinFunctionObject { - NodeCount (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NodeCount(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - INode node = ((ESNode) thisObject).getNode (); - return new ESNumber ((double) node.numberOfNodes ()); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + INode node = ((ESNode) thisObject).getNode(); + + return new ESNumber((double) node.numberOfNodes()); } } class NodeContains extends BuiltinFunctionObject { - NodeContains (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NodeContains(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 new ESNumber (node.contains (arguments)); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode node = (ESNode) thisObject; + + return new ESNumber(node.contains(arguments)); } } - class NodeInvalidate extends BuiltinFunctionObject { - NodeInvalidate (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 0); + NodeInvalidate(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 0); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { ESNode esn = (ESNode) thisObject; - return ESBoolean.makeBoolean (esn.invalidate (arguments)); + + return ESBoolean.makeBoolean(esn.invalidate(arguments)); } } - class NodePrefetch extends BuiltinFunctionObject { - NodePrefetch (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 0); + NodePrefetch(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 0); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { try { ESNode esn = (ESNode) thisObject; - esn.prefetchChildren (arguments); + + esn.prefetchChildren(arguments); } catch (IllegalArgumentException illarg) { throw illarg; } catch (Exception x) { // swallow exceptions in prefetchChildren } + return ESNull.theNull; } } - class NodeEditor extends BuiltinFunctionObject { - - NodeEditor (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NodeEditor(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length < 1) - throw new EcmaScriptException ("Missing argument for Node.editor(): Name of property to be edited."); - String propName = arguments[0].toString (); - ESValue propValue = thisObject.getProperty (propName, propName.hashCode ()); - if (propValue.isBooleanValue ()) - return (booleanEditor (thisObject, propName, propValue, arguments)); - if (propValue.isNumberValue ()) - return (numberEditor (thisObject, propName, propValue, arguments)); - if (propValue instanceof DatePrototype) - return (dateEditor (thisObject, propName, propValue, arguments)); - return (stringEditor (thisObject, propName, propValue, arguments)); - } - - public ESValue stringEditor (ESObject thisObject, String propName, ESValue propValue, ESValue[] arguments) - throws EcmaScriptException { - String value = null; - if (propValue == null || ESNull.theNull == propValue || ESUndefined.theUndefined == propValue) - value = ""; - else - value = HtmlEncoder.encodeFormValue (propValue.toString ()); - if (arguments.length == 2 && arguments[1].isNumberValue ()) - return new ESString (""); - else if (arguments.length == 3 && arguments[1].isNumberValue () && arguments[2].isNumberValue ()) - return new ESString (""); - return new ESString (""); - } - - public ESValue dateEditor (ESObject thisObject, String propName, ESValue propValue, ESValue[] arguments) - throws EcmaScriptException { - Date date = (Date) propValue.toJavaObject (); - DateFormat fmt = arguments.length > 1 ? new SimpleDateFormat (arguments[1].toString ()) : new SimpleDateFormat (); - return new ESString (""); - } - - public ESValue numberEditor (ESObject thisObject, String propName, ESValue propValue, ESValue[] arguments) - throws EcmaScriptException { - ESNumber esn = (ESNumber) propValue.toESNumber (); - double value = esn.doubleValue (); - if (arguments.length == 3 && arguments[1].isNumberValue () && arguments[2].isNumberValue ()) - return new ESString (getNumberChoice (propName, arguments[1].toInt32 (), arguments[2].toInt32 (), 1, value)); - else if (arguments.length == 4 && arguments[1].isNumberValue () && arguments[2].isNumberValue () && arguments[3].isNumberValue ()) - return new ESString (getNumberChoice (propName, arguments[1].doubleValue (), arguments[2].doubleValue (), arguments[3].doubleValue (), value)); - DecimalFormat fmt = new DecimalFormat (); - if (arguments.length == 2 && arguments[1].isNumberValue ()) - return new ESString (""); - else - return new ESString (""); - } - - public ESValue booleanEditor (ESObject thisObject, String propName, ESValue propValue, ESValue[] arguments) - throws EcmaScriptException { - ESBoolean esb = (ESBoolean) propValue.toESBoolean (); - String value = esb.toString (); - if (arguments.length < 2) { - String retval = ""); - return new ESString (retval.toString ()); + + String propName = arguments[0].toString(); + ESValue propValue = thisObject.getProperty(propName, propName.hashCode()); + + if (propValue.isBooleanValue()) { + return (booleanEditor(thisObject, propName, propValue, arguments)); + } + + if (propValue.isNumberValue()) { + return (numberEditor(thisObject, propName, propValue, arguments)); + } + + if (propValue instanceof DatePrototype) { + return (dateEditor(thisObject, propName, propValue, arguments)); + } + + return (stringEditor(thisObject, propName, propValue, arguments)); + } + + public ESValue stringEditor(ESObject thisObject, String propName, + ESValue propValue, ESValue[] arguments) + throws EcmaScriptException { + String value = null; + + if ((propValue == null) || (ESNull.theNull == propValue) || + (ESUndefined.theUndefined == propValue)) { + value = ""; + } else { + value = HtmlEncoder.encodeFormValue(propValue.toString()); + } + + if ((arguments.length == 2) && arguments[1].isNumberValue()) { + return new ESString(""); + } else if ((arguments.length == 3) && arguments[1].isNumberValue() && + arguments[2].isNumberValue()) { + return new ESString(""); + } + + return new ESString(""); + } + + public ESValue dateEditor(ESObject thisObject, String propName, + ESValue propValue, ESValue[] arguments) + throws EcmaScriptException { + Date date = (Date) propValue.toJavaObject(); + DateFormat fmt = (arguments.length > 1) + ? new SimpleDateFormat(arguments[1].toString()) + : new SimpleDateFormat(); + + return new ESString(""); + } + + public ESValue numberEditor(ESObject thisObject, String propName, + ESValue propValue, ESValue[] arguments) + throws EcmaScriptException { + ESNumber esn = (ESNumber) propValue.toESNumber(); + double value = esn.doubleValue(); + + if ((arguments.length == 3) && arguments[1].isNumberValue() && + arguments[2].isNumberValue()) { + return new ESString(getNumberChoice(propName, arguments[1].toInt32(), + arguments[2].toInt32(), 1, value)); + } else if ((arguments.length == 4) && arguments[1].isNumberValue() && + arguments[2].isNumberValue() && arguments[3].isNumberValue()) { + return new ESString(getNumberChoice(propName, arguments[1].doubleValue(), + arguments[2].doubleValue(), + arguments[3].doubleValue(), value)); + } + + DecimalFormat fmt = new DecimalFormat(); + + if ((arguments.length == 2) && arguments[1].isNumberValue()) { + return new ESString(""); + } else { + return new ESString(""); + } + } + + public ESValue booleanEditor(ESObject thisObject, String propName, + ESValue propValue, ESValue[] arguments) + throws EcmaScriptException { + ESBoolean esb = (ESBoolean) propValue.toESBoolean(); + String value = esb.toString(); + + if (arguments.length < 2) { + String retval = ""); + + return new ESString(retval.toString()); } } - class StringEditor extends BuiltinFunctionObject { - StringEditor (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + StringEditor(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - throw (new EcmaScriptException ("String.editor() wird nicht mehr unterstuetzt. Statt node.strvar.editor(...) kann node.editor(\"strvar\", ...) verwendet werden.")); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + throw (new EcmaScriptException("String.editor() wird nicht mehr unterstuetzt. Statt node.strvar.editor(...) kann node.editor(\"strvar\", ...) verwendet werden.")); } } - + class NumberEditor extends BuiltinFunctionObject { - NumberEditor (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NumberEditor(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - throw (new EcmaScriptException ("Number.editor() wird nicht mehr unterstuetzt. Statt node.nvar.editor(...) kann node.editor(\"nvar\", ...) verwendet werden.")); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + throw (new EcmaScriptException("Number.editor() wird nicht mehr unterstuetzt. Statt node.nvar.editor(...) kann node.editor(\"nvar\", ...) verwendet werden.")); } } - - class BooleanEditor extends BuiltinFunctionObject { - BooleanEditor (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + + class BooleanEditor extends BuiltinFunctionObject { + BooleanEditor(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - throw (new EcmaScriptException ("Boolean.editor() wird nicht mehr unterstuetzt. Statt node.boolvar.editor(...) kann node.editor(\"boolvar\", ...) verwendet werden.")); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + throw (new EcmaScriptException("Boolean.editor() wird nicht mehr unterstuetzt. Statt node.boolvar.editor(...) kann node.editor(\"boolvar\", ...) verwendet werden.")); } } @@ -373,74 +579,88 @@ public final class HopExtension { DatePrototypeFormat(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 0); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - DatePrototype aDate = (DatePrototype) thisObject; - DateFormat df = arguments.length > 0 ? - new SimpleDateFormat (arguments[0].toString ()) : - new SimpleDateFormat (); - df.setTimeZone(TimeZone.getDefault()); - return (aDate.toJavaObject() == null) ? - new ESString(""): - new ESString(df.format((Date) aDate.toJavaObject())); + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + DatePrototype aDate = (DatePrototype) thisObject; + + DateFormat df = (arguments.length > 0) + ? new SimpleDateFormat(arguments[0].toString()) + : new SimpleDateFormat(); + + df.setTimeZone(TimeZone.getDefault()); + + return (aDate.toJavaObject() == null) ? new ESString("") + : new ESString(df.format((Date) aDate.toJavaObject())); } } - // previously in FESI.Data.NumberObject - private Hashtable formatTable = new Hashtable (); class NumberPrototypeFormat extends BuiltinFunctionObject { NumberPrototypeFormat(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 0); - } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESObject aNumber = (ESObject) thisObject; - String fstr = "#,##0.00"; - if (arguments.length >= 1) - fstr = arguments[0].toString (); - DecimalFormat df = (DecimalFormat) formatTable.get (fstr); - if (df == null) { - df = new DecimalFormat (fstr); - formatTable.put (fstr, df); - } - return new ESString(df.format(aNumber.doubleValue ())); + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESObject aNumber = (ESObject) thisObject; + String fstr = "#,##0.00"; + + if (arguments.length >= 1) { + fstr = arguments[0].toString(); + } + + DecimalFormat df = (DecimalFormat) formatTable.get(fstr); + + if (df == null) { + df = new DecimalFormat(fstr); + formatTable.put(fstr, df); + } + + return new ESString(df.format(aNumber.doubleValue())); } } - class StringTrim extends BuiltinFunctionObject { - StringTrim (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return new ESString ( thisObject.toString().trim() ); - } - } + class StringTrim extends BuiltinFunctionObject { + StringTrim(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + return new ESString(thisObject.toString().trim()); + } + } class GlobalGetProperty extends BuiltinFunctionObject { - GlobalGetProperty (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalGetProperty(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length == 0) - return new ESString (""); - String defval = (arguments.length > 1) ? arguments[1].toString () : ""; - return new ESString (app.getProperty (arguments[0].toString (), defval)); - + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if (arguments.length == 0) { + return new ESString(""); + } + + String defval = (arguments.length > 1) ? arguments[1].toString() : ""; + + return new ESString(app.getProperty(arguments[0].toString(), defval)); } } class GlobalAuthenticate extends BuiltinFunctionObject { - GlobalAuthenticate (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalAuthenticate(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length != 2) - return ESBoolean.makeBoolean (false); - return ESBoolean.makeBoolean (app.authenticate (arguments[0].toString (), arguments[1].toString ())); + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if (arguments.length != 2) { + return ESBoolean.makeBoolean(false); + } + + return ESBoolean.makeBoolean(app.authenticate(arguments[0].toString(), + arguments[1].toString())); } } @@ -448,15 +668,20 @@ public final class HopExtension { * Get a parsed Skin from an app-managed skin text */ class GlobalCreateSkin extends BuiltinFunctionObject { - GlobalCreateSkin (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalCreateSkin(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length != 1 || ESNull.theNull.equals (arguments[0])) - throw new EcmaScriptException ("createSkin must be called with one String argument"); - String str = arguments[0].toString (); - Skin skin = new Skin (str, app); - return new ESWrapper (skin, evaluator); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if ((arguments.length != 1) || ESNull.theNull.equals(arguments[0])) { + throw new EcmaScriptException("createSkin must be called with one String argument"); + } + + String str = arguments[0].toString(); + Skin skin = new Skin(str, app); + + return new ESWrapper(skin, evaluator); } } @@ -466,382 +691,498 @@ public final class HopExtension { class RenderSkin extends BuiltinFunctionObject { boolean global; boolean asString; - RenderSkin (String name, Evaluator evaluator, FunctionPrototype fp, boolean global, boolean asString) { - super (fp, evaluator, name, 1); + + RenderSkin(String name, Evaluator evaluator, FunctionPrototype fp, + boolean global, boolean asString) { + super(fp, evaluator, name, 1); this.global = global; this.asString = asString; } - public ESValue callFunction (ESObject thisObj, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length < 1 || arguments.length > 2 || arguments[0] ==null || arguments[0] == ESNull.theNull) - throw new EcmaScriptException ("renderSkin requires one argument containing the skin name and an optional parameter object argument"); + + public ESValue callFunction(ESObject thisObj, ESValue[] arguments) + throws EcmaScriptException { + if ((arguments.length < 1) || (arguments.length > 2) || + (arguments[0] == null) || (arguments[0] == ESNull.theNull)) { + throw new EcmaScriptException("renderSkin requires one argument containing the skin name and an optional parameter object argument"); + } + try { Skin skin = null; ESObject thisObject = global ? null : thisObj; HashMap params = null; - if (arguments.length > 1 && arguments[1] instanceof ESObject) { - // create an parameter object to pass to the skin + + if ((arguments.length > 1) && arguments[1] instanceof ESObject) { + // create an parameter object to pass to the skin ESObject paramObject = (ESObject) arguments[1]; - params = new HashMap (); - for (Enumeration en=paramObject.getProperties(); en.hasMoreElements(); ) { + + params = new HashMap(); + + for (Enumeration en = paramObject.getProperties(); + en.hasMoreElements();) { String propname = (String) en.nextElement(); - params.put (propname, paramObject.getProperty (propname, propname.hashCode()).toJavaObject()); + + params.put(propname, + paramObject.getProperty(propname, propname.hashCode()) + .toJavaObject()); } } // first, see if the first argument already is a skin object. If not, it's the name of the skin to be called if (arguments[0] instanceof ESWrapper) { - Object obj = ((ESWrapper) arguments[0]).toJavaObject (); - if (obj instanceof Skin) + Object obj = ((ESWrapper) arguments[0]).toJavaObject(); + + if (obj instanceof Skin) { skin = (Skin) obj; + } } // retrieve res.skinpath, an array of objects that tell us where to look for skins // (strings for directory names and INodes for internal, db-stored skinsets) ResponseTrans res = engine.getResponse(); - Object[] skinpath = res.getSkinpath (); + Object[] skinpath = res.getSkinpath(); // ready... retrieve the skin and render it. - Object javaObject = thisObject == null ? null : thisObject.toJavaObject (); + Object javaObject = (thisObject == null) ? null : thisObject.toJavaObject(); + if (skin == null) { - String skinid = app.getPrototypeName(javaObject)+"/"+arguments[0].toString (); - skin = res.getCachedSkin (skinid); + String skinid = app.getPrototypeName(javaObject) + "/" + + arguments[0].toString(); + + skin = res.getCachedSkin(skinid); + if (skin == null) { - skin = app.getSkin (javaObject, arguments[0].toString (), skinpath); - res.cacheSkin (skinid, skin); + skin = app.getSkin(javaObject, arguments[0].toString(), skinpath); + res.cacheSkin(skinid, skin); } } - if (asString) - res.pushStringBuffer (); - if (skin != null) - skin.render (engine.getRequestEvaluator(), javaObject, params); - else - res.write ("[Skin not found: "+arguments[0]+"]"); - if (asString) - return new ESString (res.popStringBuffer ()); + + if (asString) { + res.pushStringBuffer(); + } + + if (skin != null) { + skin.render(engine.getRequestEvaluator(), javaObject, params); + } else { + res.write("[Skin not found: " + arguments[0] + "]"); + } + + if (asString) { + return new ESString(res.popStringBuffer()); + } } catch (RedirectException redir) { // let redirect pass through throw redir; } catch (Exception x) { - x.printStackTrace (); - throw new EcmaScriptException ("renderSkin: "+x); + x.printStackTrace(); + throw new EcmaScriptException("renderSkin: " + x); } + return ESNull.theNull; } } - class GlobalGetAge extends BuiltinFunctionObject { - GlobalGetAge (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalGetAge(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length != 1 || !(arguments[0] instanceof DatePrototype)) - throw new EcmaScriptException ("Invalid arguments for function getAge(Date)"); - - Date d = (Date) arguments[0].toJavaObject (); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if ((arguments.length != 1) || !(arguments[0] instanceof DatePrototype)) { + throw new EcmaScriptException("Invalid arguments for function getAge(Date)"); + } + + Date d = (Date) arguments[0].toJavaObject(); + try { - long then = d.getTime () / 60000l; - long now = System.currentTimeMillis () / 60000l; - StringBuffer age = new StringBuffer (); + long then = d.getTime() / 60000L; + long now = System.currentTimeMillis() / 60000L; + StringBuffer age = new StringBuffer(); String divider = "vor "; long diff = now - then; long days = diff / 1440; + if (days > 0) { - age.append (days > 1 ? divider+days+ " Tagen" : divider+"1 Tag"); + age.append((days > 1) ? (divider + days + " Tagen") : (divider + + "1 Tag")); divider = ", "; } + long hours = (diff % 1440) / 60; + if (hours > 0) { - age.append (hours > 1 ? divider+hours+ " Stunden" : divider+"1 Stunde"); + age.append((hours > 1) ? (divider + hours + " Stunden") + : (divider + "1 Stunde")); divider = ", "; } + long minutes = diff % 60; - if (minutes > 0) - age.append (minutes > 1 ? divider+minutes+ " Minuten" : divider+"1 Minute"); - return new ESString (age.toString ()); - + + if (minutes > 0) { + age.append((minutes > 1) ? (divider + minutes + " Minuten") + : (divider + "1 Minute")); + } + + return new ESString(age.toString()); } catch (Exception e) { - app.logEvent ("Error formatting date: "+e); - e.printStackTrace (); - return new ESString (""); + app.logEvent("Error formatting date: " + e); + e.printStackTrace(); + + return new ESString(""); } } } class GlobalGetURL extends BuiltinFunctionObject { - GlobalGetURL (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalGetURL(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length < 1) - return ESNull.theNull; + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if (arguments.length < 1) { + return ESNull.theNull; + } + try { - URL url = new URL (arguments[0].toString ()); - URLConnection con = url.openConnection (); + URL url = new URL(arguments[0].toString()); + URLConnection con = url.openConnection(); + // do we have if-modified-since or etag headers to set? if (arguments.length > 1) { if (arguments[1] instanceof DatePrototype) { Date date = (Date) arguments[1].toJavaObject(); + con.setIfModifiedSince(date.getTime()); - SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.UK); + + SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", + Locale.UK); + format.setTimeZone(TimeZone.getTimeZone("GMT")); con.setRequestProperty("If-Modified-Since", format.format(date)); } else if (arguments[1] != null) { - con.setRequestProperty ("If-None-Match", arguments[1].toString()); + con.setRequestProperty("If-None-Match", arguments[1].toString()); } } + + String httpUserAgent = app.getProperty("httpUserAgent"); + + if (httpUserAgent != null) { + con.setRequestProperty("User-Agent", httpUserAgent); + } + con.setAllowUserInteraction(false); - String filename = url.getFile (); - String contentType = con.getContentType (); - long lastmod = con.getLastModified (); - String etag = con.getHeaderField ("ETag"); + + String filename = url.getFile(); + String contentType = con.getContentType(); + long lastmod = con.getLastModified(); + String etag = con.getHeaderField("ETag"); int length = con.getContentLength(); int resCode = 0; - if (con instanceof HttpURLConnection) + + if (con instanceof HttpURLConnection) { resCode = ((HttpURLConnection) con).getResponseCode(); - ByteArrayOutputStream body = new ByteArrayOutputStream (); - if (length != 0 && resCode != 304) { - InputStream in = new BufferedInputStream(con.getInputStream ()); + } + + ByteArrayOutputStream body = new ByteArrayOutputStream(); + + if ((length != 0) && (resCode != 304)) { + InputStream in = new BufferedInputStream(con.getInputStream()); byte[] b = new byte[1024]; int read; - while ((read = in.read (b)) > -1) - body.write (b, 0, read); - in.close (); + + while ((read = in.read(b)) > -1) + body.write(b, 0, read); + + in.close(); } - MimePart mime = new MimePart (filename, body.toByteArray(), contentType); - if (lastmod > 0) + + MimePart mime = new MimePart(filename, body.toByteArray(), contentType); + + if (lastmod > 0) { mime.lastModified = new Date(lastmod); + } + mime.eTag = etag; - return ESLoader.normalizeObject (mime, evaluator); - } catch (Exception ignore) {} - return ESNull.theNull; + + return ESLoader.normalizeObject(mime, evaluator); + } catch (Exception ignore) { + } + + return ESNull.theNull; } } class GlobalEncode extends BuiltinFunctionObject { - GlobalEncode (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalEncode(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length < 1) - return ESNull.theNull; - return new ESString (HtmlEncoder.encodeAll (arguments[0].toString ())); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if (arguments.length < 1) { + return ESNull.theNull; + } + + return new ESString(HtmlEncoder.encodeAll(arguments[0].toString())); } } class GlobalEncodeXml extends BuiltinFunctionObject { - GlobalEncodeXml (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalEncodeXml(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length < 1) - return ESNull.theNull; - return new ESString (HtmlEncoder.encodeXml (arguments[0].toString ())); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if (arguments.length < 1) { + return ESNull.theNull; + } + + return new ESString(HtmlEncoder.encodeXml(arguments[0].toString())); } } class GlobalEncodeForm extends BuiltinFunctionObject { - GlobalEncodeForm (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalEncodeForm(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length < 1) - return ESNull.theNull; - return new ESString (HtmlEncoder.encodeFormValue (arguments[0].toString ())); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if (arguments.length < 1) { + return ESNull.theNull; + } + + return new ESString(HtmlEncoder.encodeFormValue(arguments[0].toString())); } } // strip tags from XML/HTML text class GlobalStripTags extends BuiltinFunctionObject { - GlobalStripTags (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalStripTags(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length < 1) - return ESNull.theNull; - StringBuffer b = new StringBuffer (); - char[] c = arguments[0].toString ().toCharArray (); - boolean inTag = false; - for (int i=0; i') inTag = false; + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if (arguments.length < 1) { + return ESNull.theNull; } - return new ESString (b.toString ()); + + StringBuffer b = new StringBuffer(); + char[] c = arguments[0].toString().toCharArray(); + boolean inTag = false; + + for (int i = 0; i < c.length; i++) { + if (c[i] == '<') { + inTag = true; + } + + if (!inTag) { + b.append(c[i]); + } + + if (c[i] == '>') { + inTag = false; + } + } + + return new ESString(b.toString()); } } class GlobalFormat extends BuiltinFunctionObject { - GlobalFormat (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalFormat(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length < 1) - return ESNull.theNull; - return new ESString (HtmlEncoder.encode (arguments[0].toString ())); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if (arguments.length < 1) { + return ESNull.theNull; + } + + return new ESString(HtmlEncoder.encode(arguments[0].toString())); } } class GlobalGetXmlDocument extends BuiltinFunctionObject { - GlobalGetXmlDocument (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalGetXmlDocument(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { try { - Object p = arguments[0].toJavaObject (); - Object doc = helma.util.XmlUtils.parseXml (p); - return ESLoader.normalizeObject (doc, evaluator); + Object p = arguments[0].toJavaObject(); + Object doc = helma.util.XmlUtils.parseXml(p); + + return ESLoader.normalizeObject(doc, evaluator); } catch (Exception noluck) { - app.logEvent ("Error creating XML document: "+noluck); + app.logEvent("Error creating XML document: " + noluck); } + return ESNull.theNull; } } class GlobalGetHtmlDocument extends BuiltinFunctionObject { - GlobalGetHtmlDocument (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalGetHtmlDocument(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { try { - Object p = arguments[0].toJavaObject (); - Object doc = helma.util.XmlUtils.parseHtml (p); - return ESLoader.normalizeObject (doc, evaluator); + Object p = arguments[0].toJavaObject(); + Object doc = helma.util.XmlUtils.parseHtml(p); + + return ESLoader.normalizeObject(doc, evaluator); } catch (Exception noluck) { - app.logEvent ("Error creating HTML document: "+noluck); + app.logEvent("Error creating HTML document: " + noluck); } + return ESNull.theNull; } } class GlobalJDOM extends BuiltinFunctionObject { - GlobalJDOM (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + GlobalJDOM(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { try { - Class.forName ("org.w3c.dom.Document"); - org.w3c.dom.Document doc = (org.w3c.dom.Document) arguments[0].toJavaObject (); - Class.forName ("org.jdom.input.DOMBuilder"); - org.jdom.input.DOMBuilder builder = new org.jdom.input.DOMBuilder (); - return ESLoader.normalizeObject (builder.build (doc), evaluator); + Class.forName("org.w3c.dom.Document"); + + org.w3c.dom.Document doc = (org.w3c.dom.Document) arguments[0].toJavaObject(); + + Class.forName("org.jdom.input.DOMBuilder"); + + org.jdom.input.DOMBuilder builder = new org.jdom.input.DOMBuilder(); + + return ESLoader.normalizeObject(builder.build(doc), evaluator); } catch (Exception noluck) { - app.logEvent ("Error building JDOM document: "+noluck); + app.logEvent("Error building JDOM document: " + noluck); } + return ESNull.theNull; } } class NodeSetParent extends BuiltinFunctionObject { - NodeSetParent (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 2); + NodeSetParent(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 2); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESNode node = (ESNode) thisObject; - return ESBoolean.makeBoolean (node.setParent (arguments)); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode node = (ESNode) thisObject; + + return ESBoolean.makeBoolean(node.setParent(arguments)); } } class NodeClearCache extends BuiltinFunctionObject { - NodeClearCache (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 2); + NodeClearCache(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 2); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESNode node = (ESNode) thisObject; - return ESBoolean.makeBoolean (node.clearCache ()); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode node = (ESNode) thisObject; + + return ESBoolean.makeBoolean(node.clearCache()); } } class NodeHref extends BuiltinFunctionObject { - NodeHref (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + NodeHref(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - Object elem = thisObject.toJavaObject (); - String tmpname = arguments.length == 0 ? "" : arguments[0].toString (); - String basicHref =app.getNodeHref (elem, tmpname); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + Object elem = thisObject.toJavaObject(); + String tmpname = (arguments.length == 0) ? "" : arguments[0].toString(); + String basicHref = app.getNodeHref(elem, tmpname); + // check if the app.properties specify a href-function to post-process the // basic href. - String hrefFunction = app.getProperty ("hrefFunction", null); + String hrefFunction = app.getProperty("hrefFunction", null); + if (hrefFunction != null) { Object funcElem = elem; + while (funcElem != null) { - if (engine.hasFunction (funcElem, hrefFunction)) { - Object obj; - try { - obj = engine.invoke (funcElem, hrefFunction, new Object[] {basicHref}, false); - } catch (ScriptingException x) { - throw new RuntimeException ("Error in hrefFunction: "+x); - } - if (obj == null) - throw new RuntimeException ("hrefFunction "+hrefFunction+" returned null"); - basicHref = obj.toString (); + if (engine.hasFunction(funcElem, hrefFunction)) { + Object obj; + + try { + obj = engine.invoke(funcElem, hrefFunction, + new Object[] { basicHref }, false); + } catch (ScriptingException x) { + throw new RuntimeException("Error in hrefFunction: " + x); + } + + if (obj == null) { + throw new RuntimeException("hrefFunction " + hrefFunction + + " returned null"); + } + + basicHref = obj.toString(); + + break; } - funcElem = app.getParentElement (funcElem); + + funcElem = app.getParentElement(funcElem); } } + // check if the app.properties specify a href-skin to post-process the // basic href. - String hrefSkin = app.getProperty ("hrefSkin", null); + String hrefSkin = app.getProperty("hrefSkin", null); + if (hrefSkin != null) { // we need to post-process the href with a skin for this application // first, look in the object href was called on. Object skinElem = elem; Skin skin = null; + // ResponseTrans res = engine.getResponse(); // Object[] skinpath = res.getSkinpath (); - while (skin == null && skinElem != null) { - Prototype proto = app.getPrototype (skinElem); + while ((skin == null) && (skinElem != null)) { + Prototype proto = app.getPrototype(skinElem); + if (proto != null) { - skin = proto.getSkin (hrefSkin); + skin = proto.getSkin(hrefSkin); + } + + if (skin == null) { + skinElem = app.getParentElement(skinElem); } - if (skin == null) - skinElem = app.getParentElement (skinElem); } if (skin != null) { - basicHref = renderSkin (skin, basicHref, skinElem); + basicHref = renderSkin(skin, basicHref, skinElem); } } - return new ESString (basicHref); + return new ESString(basicHref); } - private String renderSkin (Skin skin, String path, Object skinElem) throws EcmaScriptException { - engine.getResponse().pushStringBuffer (); - HashMap param = new HashMap (); - param.put ("path", path); - skin.render (engine.getRequestEvaluator(), skinElem, param); - return engine.getResponse().popStringBuffer ().trim (); + + private String renderSkin(Skin skin, String path, Object skinElem) + throws EcmaScriptException { + engine.getResponse().pushStringBuffer(); + + HashMap param = new HashMap(); + + param.put("path", path); + skin.render(engine.getRequestEvaluator(), skinElem, param); + + return engine.getResponse().popStringBuffer().trim(); } } - - - private String getNumberChoice (String name, double from, double to, double step, double value) { - double l = 0.000001; - StringBuffer b = new StringBuffer (""); - return b.toString (); - } - - - } - +} diff --git a/src/helma/scripting/fesi/NodeConstructor.java b/src/helma/scripting/fesi/NodeConstructor.java index 6c9d278b..c4172e05 100644 --- a/src/helma/scripting/fesi/NodeConstructor.java +++ b/src/helma/scripting/fesi/NodeConstructor.java @@ -1,90 +1,164 @@ -// NodeConstructor.java -// Copyright (c) Hannes Wallnöfer 2000 +/* + * 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.scripting.fesi; -import helma.objectmodel.db.Node; -import helma.framework.core.*; import FESI.Data.*; import FESI.Exceptions.*; import FESI.Interpreter.*; +import helma.framework.core.*; +import helma.objectmodel.db.Node; /** * A constructor for user defined data types. This first constructs a node, sets its prototype * and invokes the scripted constructor function on it. */ - public class NodeConstructor extends BuiltinFunctionObject { + FesiEngine engine; + String typename; - FesiEngine engine; - String typename; + /** + * Creates a new NodeConstructor object. + * + * @param name ... + * @param fp ... + * @param engine ... + */ + public NodeConstructor(String name, FunctionPrototype fp, FesiEngine engine) { + super(fp, engine.getEvaluator(), name, 1); + typename = name; + this.engine = engine; + } - public NodeConstructor (String name, FunctionPrototype fp, FesiEngine engine) { - super(fp, engine.getEvaluator (), name, 1); - typename = name; - this.engine = engine; + /** + * + * + * @param thisObject ... + * @param arguments ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + return doConstruct(thisObject, arguments); + } + + /** + * + * + * @param thisObject ... + * @param arguments ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESNode node = null; + Application app = engine.getApplication(); + + if ("Node".equals(typename) || "hopobject".equalsIgnoreCase(typename)) { + String nodeName = null; + + if ((arguments.length > 0) && (arguments[0] != null)) { + nodeName = arguments[0].toString(); + } + + Node n = new Node(nodeName, (String) null, app.getWrappedNodeManager()); + + node = new ESNode(engine.getPrototype("hopobject"), this.evaluator, n, engine); + engine.putNodeWrapper(node.getNode(), node); + } else { + // Typed nodes are instantiated as helma.objectmodel.db.Node from the beginning + // even if we don't know yet if they are going to be stored in a database. The reason + // is that we want to be able to use the specail features like subnode relations even for + // transient nodes. + ObjectPrototype op = engine.getPrototype(typename); + Node n = new Node(typename, typename, app.getWrappedNodeManager()); + + node = new ESNode(op, engine.getEvaluator(), n, engine); + node.setPrototype(typename); + node.getNode().setDbMapping(app.getDbMapping(typename)); + + try { + // first try calling "constructor", if that doesn't work, try calling a function + // with the name of the type. + // HACK: There is an incompatibility problem here, because the property + // constructor is defined as the constructor of the object by EcmaScript. + if (op.getProperty("constructor", "constructor".hashCode()) instanceof ConstructedFunctionObject) { + node.doIndirectCall(engine.getEvaluator(), node, "constructor", + arguments); + } else if (op.getProperty(typename, typename.hashCode()) instanceof ConstructedFunctionObject) { + node.doIndirectCall(engine.getEvaluator(), node, typename, arguments); + } + } catch (Exception x) { + throw new EcmaScriptException(x.toString()); + } } - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return doConstruct(thisObject, arguments); + return node; + } + + /** + * + * + * @param propertyName ... + * @param previousScope ... + * @param hash ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public ESValue getPropertyInScope(String propertyName, ScopeChain previousScope, + int hash) throws EcmaScriptException { + return super.getPropertyInScope(propertyName, previousScope, hash); + } + + /** + * + * + * @param propertyName ... + * @param hash ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public ESValue getProperty(String propertyName, int hash) + throws EcmaScriptException { + if ("prototype".equals(propertyName)) { + return engine.getPrototype(typename); } - public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESNode node = null; - Application app = engine.getApplication (); - if ("Node".equals (typename) || "hopobject".equalsIgnoreCase (typename)) { - String nodeName = null; - if (arguments.length > 0 && arguments[0] != null) - nodeName = arguments[0].toString(); - Node n = new Node (nodeName, (String) null, app.getWrappedNodeManager ()); - node = new ESNode (engine.getPrototype ("hopobject"), this.evaluator, n, engine); - engine.putNodeWrapper (node.getNode (), node); - } else { - // Typed nodes are instantiated as helma.objectmodel.db.Node from the beginning - // even if we don't know yet if they are going to be stored in a database. The reason - // is that we want to be able to use the specail features like subnode relations even for - // transient nodes. - ObjectPrototype op = engine.getPrototype (typename); - Node n = new Node (typename, typename, app.getWrappedNodeManager ()); - node = new ESNode (op, engine.getEvaluator (), n, engine); - node.setPrototype (typename); - node.getNode ().setDbMapping (app.getDbMapping (typename)); - try { - // first try calling "constructor", if that doesn't work, try calling a function - // with the name of the type. - // HACK: There is an incompatibility problem here, because the property - // constructor is defined as the constructor of the object by EcmaScript. - if (op.getProperty ("constructor", - "constructor".hashCode()) - instanceof ConstructedFunctionObject) - node.doIndirectCall (engine.getEvaluator(), node, "constructor", arguments); - else if (op.getProperty (typename, - typename.hashCode()) - instanceof ConstructedFunctionObject) - node.doIndirectCall (engine.getEvaluator(), node, typename, arguments); - } catch (Exception x) { - throw new EcmaScriptException (x.toString()); - } - } - return node; - } - - public ESValue getPropertyInScope(String propertyName, ScopeChain previousScope, int hash) throws EcmaScriptException { - return super.getPropertyInScope (propertyName, previousScope, hash); - } - - public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException { - if ("prototype".equals (propertyName)) - return engine.getPrototype (typename); - return super.getProperty(propertyName, hash); - } - - public String[] getSpecialPropertyNames() { - String ns[] = {}; - return ns; - } - - } // class NodeConstructor - + return super.getProperty(propertyName, hash); + } + /** + * + * + * @return ... + */ + public String[] getSpecialPropertyNames() { + String[] ns = { }; + return ns; + } +} + // class NodeConstructor diff --git a/src/helma/scripting/fesi/PhantomEngine.java b/src/helma/scripting/fesi/PhantomEngine.java index f453e4d1..3a0100b9 100644 --- a/src/helma/scripting/fesi/PhantomEngine.java +++ b/src/helma/scripting/fesi/PhantomEngine.java @@ -1,17 +1,32 @@ -// PhantomEngine.java -// Copyright (c) Hannes Wallnöfer 2002 +/* + * 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.scripting.fesi; import helma.scripting.ScriptingException; +/** + * + */ public final class PhantomEngine extends FesiEngine { - /** * */ - public Object invoke (Object thisObject, String functionName, Object[] args, boolean xmlrpc) throws ScriptingException { - return super.invoke (thisObject, functionName, args, xmlrpc); + public Object invoke(Object thisObject, String functionName, Object[] args, + boolean xmlrpc) throws ScriptingException { + return super.invoke(thisObject, functionName, args, xmlrpc); } - -} \ No newline at end of file +} diff --git a/src/helma/scripting/fesi/extensions/DomExtension.java b/src/helma/scripting/fesi/extensions/DomExtension.java index 61af344f..d3cb491e 100644 --- a/src/helma/scripting/fesi/extensions/DomExtension.java +++ b/src/helma/scripting/fesi/extensions/DomExtension.java @@ -1,153 +1,239 @@ -package helma.scripting.fesi.extensions; +/* + * 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$ + */ -import java.io.ByteArrayOutputStream; -import java.io.StringReader; -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; +package helma.scripting.fesi.extensions; import FESI.Data.*; import FESI.Exceptions.*; import FESI.Extensions.*; import FESI.Interpreter.*; - import helma.framework.core.Application; import helma.framework.core.RequestEvaluator; import helma.objectmodel.INode; import helma.objectmodel.db.Node; import helma.objectmodel.dom.*; import helma.scripting.fesi.ESNode; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.StringReader; -public class DomExtension extends Extension { - +/** + * + */ +public class DomExtension extends Extension { private transient Evaluator evaluator = null; + /** + * Creates a new DomExtension object. + */ public DomExtension() { super(); } - public void initializeExtension(Evaluator evaluator) throws EcmaScriptException { + /** + * + * + * @param evaluator ... + * + * @throws EcmaScriptException ... + */ + public void initializeExtension(Evaluator evaluator) + throws EcmaScriptException { this.evaluator = evaluator; + GlobalObject go = evaluator.getGlobalObject(); ObjectPrototype op = (ObjectPrototype) evaluator.getObjectPrototype(); FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); ESObject globalXml = new GlobalObjectXml("Xml", evaluator, fp); - globalXml.putHiddenProperty ("length",new ESNumber(1)); - globalXml.putHiddenProperty ("read", new XmlRead ("read", evaluator, fp, false)); - globalXml.putHiddenProperty ("readFromString", new XmlRead ("readFromString", evaluator, fp, true)); - globalXml.putHiddenProperty ("write", new XmlWrite ("write", evaluator, fp)); - globalXml.putHiddenProperty ("writeToString", new XmlWriteToString ("writeToString", evaluator, fp)); - globalXml.putHiddenProperty ("get", new XmlGet ("get", evaluator, fp)); - globalXml.putHiddenProperty ("getFromString", new XmlGetFromString ("getFromString", evaluator, fp)); - go.putHiddenProperty ("Xml", globalXml); - } - class GlobalObjectXml extends BuiltinFunctionObject { - GlobalObjectXml(String name, Evaluator evaluator, FunctionPrototype fp) { - super(fp, evaluator, name, 1); - } - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return doConstruct(thisObject, arguments); - } - public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - throw new EcmaScriptException("Xml can't be instanced"); - } - } + globalXml.putHiddenProperty("length", new ESNumber(1)); + globalXml.putHiddenProperty("read", new XmlRead("read", evaluator, fp, false)); + globalXml.putHiddenProperty("readFromString", + new XmlRead("readFromString", evaluator, fp, true)); + globalXml.putHiddenProperty("write", new XmlWrite("write", evaluator, fp)); + globalXml.putHiddenProperty("writeToString", + new XmlWriteToString("writeToString", evaluator, fp)); + globalXml.putHiddenProperty("get", new XmlGet("get", evaluator, fp)); + globalXml.putHiddenProperty("getFromString", + new XmlGetFromString("getFromString", evaluator, fp)); + go.putHiddenProperty("Xml", globalXml); + } - class XmlWrite extends BuiltinFunctionObject { - XmlWrite(String name, Evaluator evaluator, FunctionPrototype fp) { - super(fp, evaluator, name, 1); - } - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if ( arguments==null || arguments.length != 2 ) - throw new EcmaScriptException("Wrong number of arguments in Xml.write()"); - INode node = null; - try { - node = ((ESNode)arguments[0]).getNode(); - } catch ( Exception e ) { - // we definitly need a node - throw new EcmaScriptException("First argument in Xml.write() is not an hopobject"); - } - try { - File tmpFile = new File(arguments[1].toString()+".tmp."+XmlWriter.generateID()); - XmlWriter writer = new XmlWriter (tmpFile, "UTF-8"); - writer.setDatabaseMode(false); - boolean result = writer.write(node); - writer.close(); - File finalFile = new File(arguments[1].toString()); - tmpFile.renameTo (finalFile); - this.evaluator.engine.getApplication().logEvent("wrote xml to " + finalFile.getAbsolutePath() ); - } catch (IOException io) { - throw new EcmaScriptException (io.toString()); - } - return ESBoolean.makeBoolean(true); + class GlobalObjectXml extends BuiltinFunctionObject { + GlobalObjectXml(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + return doConstruct(thisObject, arguments); + } + + public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + throw new EcmaScriptException("Xml can't be instanced"); + } + + public String toString() { + try { + String parser = javax.xml.parsers.DocumentBuilderFactory.newInstance() + .getClass() + .getPackage() + .getName(); + + return "[Xml " + parser + "]"; + } catch (NullPointerException zeero) { + return "[Xml - no parser available]"; + } } } - /** - * Xml.create() is used to get a string containing the xml-content. - * Useful if Xml-content should be made public through the web. - */ + class XmlWrite extends BuiltinFunctionObject { + XmlWrite(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if ((arguments == null) || (arguments.length != 2)) { + throw new EcmaScriptException("Wrong number of arguments in Xml.write()"); + } + + INode node = null; + + try { + node = ((ESNode) arguments[0]).getNode(); + } catch (Exception e) { + // we definitly need a node + throw new EcmaScriptException("First argument in Xml.write() is not an hopobject"); + } + + try { + File tmpFile = new File(arguments[1].toString() + ".tmp." + + XmlWriter.generateID()); + XmlWriter writer = new XmlWriter(tmpFile, "UTF-8"); + + writer.setDatabaseMode(false); + + boolean result = writer.write(node); + + writer.close(); + + File finalFile = new File(arguments[1].toString()); + + tmpFile.renameTo(finalFile); + this.evaluator.engine.getApplication().logEvent("wrote xml to " + + finalFile.getAbsolutePath()); + } catch (IOException io) { + throw new EcmaScriptException(io.toString()); + } + + return ESBoolean.makeBoolean(true); + } + } + + /** + * Xml.create() is used to get a string containing the xml-content. + * Useful if Xml-content should be made public through the web. + */ class XmlWriteToString extends BuiltinFunctionObject { - XmlWriteToString(String name, Evaluator evaluator, FunctionPrototype fp) { - super(fp, evaluator, name, 1); - } - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if ( arguments==null || arguments.length==0 ) - throw new EcmaScriptException("Not enough arguments in Xml.writeToString()"); - INode node = null; - try { - node = ((ESNode)arguments[0]).getNode(); - } catch ( Exception e ) { - // we definitly need a node - throw new EcmaScriptException("argument is not an hopobject"); - } - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - XmlWriter writer = new XmlWriter (out, "UTF-8"); - // in case we ever want to limit serialization depth... - // if (arguments.length > 1 && arguments[1] instanceof ESNumber) - // writer.setMaxLevels(arguments[1].toInt32()); - writer.setDatabaseMode(false); - boolean result = writer.write(node); - writer.flush(); - return new ESString (out.toString("UTF-8")); - } catch (IOException io) { - throw new EcmaScriptException (io.toString()); - } + XmlWriteToString(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if ((arguments == null) || (arguments.length == 0)) { + throw new EcmaScriptException("Not enough arguments in Xml.writeToString()"); + } + + INode node = null; + + try { + node = ((ESNode) arguments[0]).getNode(); + } catch (Exception e) { + // we definitly need a node + throw new EcmaScriptException("argument is not an hopobject"); + } + + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + XmlWriter writer = new XmlWriter(out, "UTF-8"); + + // in case we ever want to limit serialization depth... + // if (arguments.length > 1 && arguments[1] instanceof ESNumber) + // writer.setMaxLevels(arguments[1].toInt32()); + writer.setDatabaseMode(false); + + boolean result = writer.write(node); + + writer.flush(); + + return new ESString(out.toString("UTF-8")); + } catch (IOException io) { + throw new EcmaScriptException(io.toString()); + } } } class XmlRead extends BuiltinFunctionObject { boolean fromstring; + XmlRead(String name, Evaluator evaluator, FunctionPrototype fp, boolean fromstring) { super(fp, evaluator, name, 1); - this.fromstring = fromstring; + this.fromstring = fromstring; } - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if ( arguments==null || arguments.length==0 ) - throw new EcmaScriptException("Missing arguments in Xml.read()"); - INode node = null; - try { - node = ((ESNode)arguments[1]).getNode(); - } catch ( Exception e ) { //classcast, arrayindex etc - // make sure we have a node, even if 2nd arg doesn't exist or is not a node - node = new Node ( (String)null, (String)null, this.evaluator.engine.getApplication().getWrappedNodeManager() ); - } - try { - XmlReader reader = new XmlReader (); - INode result = null; - if (fromstring) - result = reader.read (new StringReader (arguments[0].toString()),node); - else - result = reader.read (new File(arguments[0].toString()),node); - return this.evaluator.engine.getNodeWrapper (result); - } catch ( NoClassDefFoundError e ) { - throw new EcmaScriptException ("Can't load XML parser:"+e); - } catch ( Exception f ) { - throw new EcmaScriptException (f.toString()); - } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if ((arguments == null) || (arguments.length == 0)) { + throw new EcmaScriptException("Missing arguments in Xml.read()"); + } + + INode node = null; + + try { + node = ((ESNode) arguments[1]).getNode(); + } catch (Exception e) { //classcast, arrayindex etc + + // make sure we have a node, even if 2nd arg doesn't exist or is not a node + node = new Node((String) null, (String) null, + this.evaluator.engine.getApplication() + .getWrappedNodeManager()); + } + + try { + XmlReader reader = new XmlReader(); + INode result = null; + + if (fromstring) { + result = reader.read(new StringReader(arguments[0].toString()), node); + } else { + result = reader.read(new File(arguments[0].toString()), node); + } + + return this.evaluator.engine.getNodeWrapper(result); + } catch (NoClassDefFoundError e) { + throw new EcmaScriptException("Can't load XML parser:" + e); + } catch (Exception f) { + throw new EcmaScriptException(f.toString()); + } } } @@ -155,24 +241,33 @@ public class DomExtension extends Extension { XmlGet(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if ( arguments==null || arguments.length==0 ) - throw new EcmaScriptException("Xml.get() needs a location as an argument"); - try { - XmlConverter converter; - if ( arguments.length>1 ) { - converter = new XmlConverter (arguments[1].toString()); - } else { - converter = new XmlConverter (); - } - INode node = new helma.objectmodel.db.Node ( (String)null, (String)null, this.evaluator.engine.getApplication().getWrappedNodeManager() ); - INode result = converter.convert (arguments[0].toString(),node); - return this.evaluator.engine.getNodeWrapper(result); - } catch ( NoClassDefFoundError e ) { - throw new EcmaScriptException("Can't load dom-capable xml parser."); - } catch ( RuntimeException f ) { - throw new EcmaScriptException(f.toString()); - } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if ((arguments == null) || (arguments.length == 0)) { + throw new EcmaScriptException("Xml.get() needs a location as an argument"); + } + + try { + XmlConverter converter; + + if (arguments.length > 1) { + converter = new XmlConverter(arguments[1].toString()); + } else { + converter = new XmlConverter(); + } + + INode node = new helma.objectmodel.db.Node((String) null, (String) null, + this.evaluator.engine.getApplication() + .getWrappedNodeManager()); + INode result = converter.convert(arguments[0].toString(), node); + + return this.evaluator.engine.getNodeWrapper(result); + } catch (NoClassDefFoundError e) { + throw new EcmaScriptException("Can't load dom-capable xml parser."); + } catch (RuntimeException f) { + throw new EcmaScriptException(f.toString()); + } } } @@ -180,27 +275,33 @@ public class DomExtension extends Extension { XmlGetFromString(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if ( arguments==null || arguments.length==0 ) - throw new EcmaScriptException("Xml.getFromString() needs an XML string as parameter"); - try { - XmlConverter converter; - if ( arguments.length>1 ) { - converter = new XmlConverter (arguments[1].toString()); - } else { - converter = new XmlConverter (); - } - INode node = new helma.objectmodel.db.Node ( (String)null, (String)null, this.evaluator.engine.getApplication().getWrappedNodeManager() ); - INode result = converter.convertFromString (arguments[0].toString(),node); - return this.evaluator.engine.getNodeWrapper(result); - } catch ( NoClassDefFoundError e ) { - throw new EcmaScriptException("Can't load dom-capable xml parser."); - } catch ( RuntimeException f ) { - throw new EcmaScriptException(f.toString()); - } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + if ((arguments == null) || (arguments.length == 0)) { + throw new EcmaScriptException("Xml.getFromString() needs an XML string as parameter"); + } + + try { + XmlConverter converter; + + if (arguments.length > 1) { + converter = new XmlConverter(arguments[1].toString()); + } else { + converter = new XmlConverter(); + } + + INode node = new helma.objectmodel.db.Node((String) null, (String) null, + this.evaluator.engine.getApplication() + .getWrappedNodeManager()); + INode result = converter.convertFromString(arguments[0].toString(), node); + + return this.evaluator.engine.getNodeWrapper(result); + } catch (NoClassDefFoundError e) { + throw new EcmaScriptException("Can't load dom-capable xml parser."); + } catch (RuntimeException f) { + throw new EcmaScriptException(f.toString()); + } } } - } - - diff --git a/src/helma/scripting/fesi/extensions/ESMail.java b/src/helma/scripting/fesi/extensions/ESMail.java index c8d1c3ea..13aedd08 100644 --- a/src/helma/scripting/fesi/extensions/ESMail.java +++ b/src/helma/scripting/fesi/extensions/ESMail.java @@ -1,207 +1,353 @@ -// ESMail.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - +/* + * 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.scripting.fesi.extensions; -import javax.mail.Session; -import javax.mail.Multipart; -import javax.mail.Address; -import javax.mail.Transport; -import javax.mail.Message; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeUtility; -import javax.mail.internet.MimeMultipart; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.AddressException; -import javax.activation.*; +import FESI.Data.*; +import FESI.Exceptions.*; +import FESI.Interpreter.*; +import helma.util.*; import java.io.*; import java.util.*; -import helma.util.*; -import FESI.Data.*; -import FESI.Interpreter.*; -import FESI.Exceptions.*; +import javax.activation.*; +import javax.mail.Address; +import javax.mail.Message; +import javax.mail.Multipart; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; +import javax.mail.internet.MimeUtility; /** - * A JavaScript wrapper around a JavaMail message class to send + * A JavaScript wrapper around a JavaMail message class to send * mail via SMTP from Helma */ - public class ESMail extends ESObject implements Serializable { - + public static final int OK = 0; + public static final int SUBJECT = 10; + public static final int TEXT = 11; + public static final int MIMEPART = 12; + public static final int TO = 20; + public static final int CC = 21; + public static final int BCC = 22; + public static final int FROM = 23; + public static final int REPLYTO = 24; + public static final int SEND = 30; MailExtension mailx; Properties mprops; MimeMessage message; Multipart multipart; StringBuffer buffer; - int status; - public static final int OK=0; - public static final int SUBJECT=10; - public static final int TEXT=11; - public static final int MIMEPART=12; - public static final int TO=20; - public static final int CC=21; - public static final int BCC=22; - public static final int FROM=23; - public static final int REPLYTO=24; - public static final int SEND=30; + /** + * Creates a new ESMail object. + * + * @param mailx ... + */ + public ESMail(MailExtension mailx) { + super(mailx.esMailPrototype, mailx.eval); + this.status = OK; + this.mailx = mailx; + this.mprops = mailx.mprops; - public ESMail (MailExtension mailx) { - super (mailx.esMailPrototype, mailx.eval); - this.status = OK; - this.mailx = mailx; - this.mprops = mailx.mprops; + // create some properties and get the default Session + try { + Properties props = new Properties(); - // create some properties and get the default Session - try { - Properties props = new Properties(); - props.put ("mail.smtp.host", mprops.getProperty ("smtp", "mail")); + props.put("mail.smtp.host", mprops.getProperty("smtp", "mail")); - Session session = Session.getDefaultInstance(props, null); - message = new MimeMessage (session); - } catch (Throwable t) { - this.evaluator.engine.getApplication().logEvent ("Error in mail constructor: "+t); - } + Session session = Session.getDefaultInstance(props, null); + + message = new MimeMessage(session); + } catch (Throwable t) { + this.evaluator.engine.getApplication().logEvent("Error in mail constructor: " + + t); + } } - public void setStatus (int status) { - // Only register the first error that occurrs - if (this.status == 0) - this.status = status; + /** + * + * + * @param status ... + */ + public void setStatus(int status) { + // Only register the first error that occurrs + if (this.status == 0) { + this.status = status; + } } - public int getStatus () { - return status; + /** + * + * + * @return ... + */ + public int getStatus() { + return status; } - public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException { - if ("status".equalsIgnoreCase (propertyName)) - return new ESNumber (status); - return super.getProperty (propertyName, hash); + /** + * + * + * @param propertyName ... + * @param hash ... + * + * @return ... + * + * @throws EcmaScriptException ... + */ + public ESValue getProperty(String propertyName, int hash) + throws EcmaScriptException { + if ("status".equalsIgnoreCase(propertyName)) { + return new ESNumber(status); + } + + return super.getProperty(propertyName, hash); } /** * */ + public void setText(ESValue val) throws Exception { + if (buffer == null) { + buffer = new StringBuffer(); + } - public void setText (ESValue val) throws Exception { - if (buffer == null) - buffer = new StringBuffer (); - if (val != null) - buffer.append (val.toString ()); + if (val != null) { + buffer.append(val.toString()); + } } - public void addPart (ESValue val[]) throws Exception { - if (val == null || val.length == 0 || val.length > 2) - throw new IOException ("mail.addPart called with wrong number of arguments."); - if (multipart == null) { - multipart = new MimeMultipart (); - } - MimeBodyPart part = new MimeBodyPart (); - Object obj = val[0].toJavaObject (); - if (obj instanceof String) { - part.setContent (obj.toString (), "text/plain"); - } else if (obj instanceof File) { - FileDataSource source = new FileDataSource ((File) obj); - part.setDataHandler (new DataHandler (source)); - } else if (obj instanceof MimePart) { - MimePartDataSource source = new MimePartDataSource ((MimePart) obj); - part.setDataHandler (new DataHandler (source)); - } - // check if an explicit file name was given for this part - if (val.length == 2) try { - part.setFileName (val[1].toString()); - } catch (Exception x) { - // FIXME: error setting file name ... should we ignore this or throw an exception? - } - multipart.addBodyPart (part); + /** + * + * + * @param val ... + * + * @throws Exception ... + * @throws IOException ... + */ + public void addPart(ESValue[] val) throws Exception { + if ((val == null) || (val.length == 0) || (val.length > 2)) { + throw new IOException("mail.addPart called with wrong number of arguments."); + } + + if (multipart == null) { + multipart = new MimeMultipart(); + } + + MimeBodyPart part = new MimeBodyPart(); + Object obj = val[0].toJavaObject(); + + if (obj instanceof String) { + part.setContent(obj.toString(), "text/plain"); + } else if (obj instanceof File) { + FileDataSource source = new FileDataSource((File) obj); + + part.setDataHandler(new DataHandler(source)); + } else if (obj instanceof MimePart) { + MimePartDataSource source = new MimePartDataSource((MimePart) obj); + + part.setDataHandler(new DataHandler(source)); + } + + // check if an explicit file name was given for this part + if (val.length == 2) { + try { + part.setFileName(val[1].toString()); + } catch (Exception x) { + // FIXME: error setting file name ... should we ignore this or throw an exception? + } + } + + multipart.addBodyPart(part); } - public void setSubject (ESValue val) throws Exception { - if (val == null) - return; - message.setSubject (MimeUtility.encodeWord (val.toString ())); + /** + * + * + * @param val ... + * + * @throws Exception ... + */ + public void setSubject(ESValue val) throws Exception { + if (val == null) { + return; + } + + message.setSubject(MimeUtility.encodeWord(val.toString())); } - public void setReplyTo (ESValue add) throws Exception { - String addstring = add.toString (); - if (addstring.indexOf ("@") < 0) - throw new AddressException (); - Address replyTo[] = new Address[1]; - replyTo[0] = new InternetAddress (addstring); - message.setReplyTo (replyTo); + /** + * + * + * @param add ... + * + * @throws Exception ... + * @throws AddressException ... + */ + public void setReplyTo(ESValue add) throws Exception { + String addstring = add.toString(); + + if (addstring.indexOf("@") < 0) { + throw new AddressException(); + } + + Address[] replyTo = new Address[1]; + + replyTo[0] = new InternetAddress(addstring); + message.setReplyTo(replyTo); } - public void setFrom (ESValue add[]) throws Exception { - String addstring = add[0].toString (); - if (addstring.indexOf ("@") < 0) - throw new AddressException (); - Address address = null; - if (add.length > 1) - address = new InternetAddress (addstring, MimeUtility.encodeWord (add[1].toString ())); - else - address = new InternetAddress (addstring); - message.setFrom (address); + /** + * + * + * @param add ... + * + * @throws Exception ... + * @throws AddressException ... + */ + public void setFrom(ESValue[] add) throws Exception { + String addstring = add[0].toString(); + + if (addstring.indexOf("@") < 0) { + throw new AddressException(); + } + + Address address = null; + + if (add.length > 1) { + address = new InternetAddress(addstring, + MimeUtility.encodeWord(add[1].toString())); + } else { + address = new InternetAddress(addstring); + } + + message.setFrom(address); } - public void addTo (ESValue add[]) throws Exception { - String addstring = add[0].toString (); - if (addstring.indexOf ("@") < 0) - throw new AddressException (); - Address address = null; - if (add.length > 1) - address = new InternetAddress (addstring, MimeUtility.encodeWord (add[1].toString ())); - else - address = new InternetAddress (addstring); - message.addRecipient (Message.RecipientType.TO, address); + /** + * + * + * @param add ... + * + * @throws Exception ... + * @throws AddressException ... + */ + public void addTo(ESValue[] add) throws Exception { + String addstring = add[0].toString(); + + if (addstring.indexOf("@") < 0) { + throw new AddressException(); + } + + Address address = null; + + if (add.length > 1) { + address = new InternetAddress(addstring, + MimeUtility.encodeWord(add[1].toString())); + } else { + address = new InternetAddress(addstring); + } + + message.addRecipient(Message.RecipientType.TO, address); } - public void addCC (ESValue add[]) throws Exception { - String addstring = add[0].toString (); - if (addstring.indexOf ("@") < 0) - throw new AddressException (); - Address address = null; - if (add.length > 1) - address = new InternetAddress (addstring, MimeUtility.encodeWord (add[1].toString ())); - else - address = new InternetAddress (addstring); - message.addRecipient (Message.RecipientType.CC, address); + /** + * + * + * @param add ... + * + * @throws Exception ... + * @throws AddressException ... + */ + public void addCC(ESValue[] add) throws Exception { + String addstring = add[0].toString(); + + if (addstring.indexOf("@") < 0) { + throw new AddressException(); + } + + Address address = null; + + if (add.length > 1) { + address = new InternetAddress(addstring, + MimeUtility.encodeWord(add[1].toString())); + } else { + address = new InternetAddress(addstring); + } + + message.addRecipient(Message.RecipientType.CC, address); } - public void addBCC (ESValue add[]) throws Exception { - String addstring = add[0].toString (); - if (addstring.indexOf ("@") < 0) - throw new AddressException (); - Address address = null; - if (add.length > 1) - address = new InternetAddress (addstring, MimeUtility.encodeWord (add[1].toString ())); - else - address = new InternetAddress (addstring); - message.addRecipient (Message.RecipientType.BCC, address); + /** + * + * + * @param add ... + * + * @throws Exception ... + * @throws AddressException ... + */ + public void addBCC(ESValue[] add) throws Exception { + String addstring = add[0].toString(); + + if (addstring.indexOf("@") < 0) { + throw new AddressException(); + } + + Address address = null; + + if (add.length > 1) { + address = new InternetAddress(addstring, + MimeUtility.encodeWord(add[1].toString())); + } else { + address = new InternetAddress(addstring); + } + + message.addRecipient(Message.RecipientType.BCC, address); } - public void send () throws Exception { - if (buffer != null) { - // if we also have a multipart body, add - // plain string as first part to it. - if (multipart != null) { - MimeBodyPart part = new MimeBodyPart (); - part.setContent (buffer.toString (), "text/plain"); - multipart.addBodyPart (part, 0); - message.setContent (multipart); - } else { - message.setText (buffer.toString ()); - } - } else if (multipart != null) - message.setContent (multipart); - else - message.setText (""); - Transport.send (message); + /** + * + * + * @throws Exception ... + */ + public void send() throws Exception { + if (buffer != null) { + // if we also have a multipart body, add + // plain string as first part to it. + if (multipart != null) { + MimeBodyPart part = new MimeBodyPart(); + + part.setContent(buffer.toString(), "text/plain"); + multipart.addBodyPart(part, 0); + message.setContent(multipart); + } else { + message.setText(buffer.toString()); + } + } else if (multipart != null) { + message.setContent(multipart); + } else { + message.setText(""); + } + + Transport.send(message); } - - } - diff --git a/src/helma/scripting/fesi/extensions/FtpExtension.java b/src/helma/scripting/fesi/extensions/FtpExtension.java index 007f5a64..0c772170 100644 --- a/src/helma/scripting/fesi/extensions/FtpExtension.java +++ b/src/helma/scripting/fesi/extensions/FtpExtension.java @@ -1,35 +1,43 @@ -// FtpExtension.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * 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.scripting.fesi.extensions; -import helma.objectmodel.*; -import FESI.Parser.*; import FESI.AST.*; -import FESI.Interpreter.*; +import FESI.Data.*; import FESI.Exceptions.*; import FESI.Extensions.*; -import FESI.Data.*; +import FESI.Interpreter.*; +import FESI.Parser.*; +import com.oroinc.net.ftp.*; +import helma.objectmodel.*; import java.io.*; -import com.oroinc.net.ftp.*; - - /** - * A FTP-client object that allows to do some FTP from HOP applications. - * FTP support is far from complete but can easily be extended if more - * functionality is needed. - * This uses the NetComponent classes from savarese.org (ex oroinc.com). - */ - + * A FTP-client object that allows to do some FTP from HOP applications. + * FTP support is far from complete but can easily be extended if more + * functionality is needed. + * This uses the NetComponent classes from savarese.org (ex oroinc.com). + */ class ESFtpClient extends ESObject { - private FTPClient ftpclient; private String server; private Exception lastError = null; private File localDir = null; - /** * Create a new FTP Client * @@ -38,35 +46,48 @@ class ESFtpClient extends ESObject { */ ESFtpClient(ESObject prototype, Evaluator evaluator, ESValue srvstr) { super(prototype, evaluator); - this.server = srvstr.toString (); + this.server = srvstr.toString(); } ESFtpClient(ESObject prototype, Evaluator evaluator) { super(prototype, evaluator); } - + /** + * + * + * @return ... + */ public String getESClassName() { return "FtpClient"; } - + + /** + * + * + * @return ... + */ public String toString() { - return "[FtpClient]"; + return "[FtpClient]"; } - + + /** + * + * + * @return ... + */ public String toDetailString() { - return "ES:[Object: builtin " + this.getClass().getName() + ":" + - this.toString() + "]"; + return "ES:[Object: builtin " + this.getClass().getName() + ":" + + this.toString() + "]"; } - - ESValue getLastError() throws EcmaScriptException { + + ESValue getLastError() throws EcmaScriptException { if (lastError == null) { return ESNull.theNull; } else { return ESLoader.normalizeValue(lastError, evaluator); } } - /** * Login to the FTP server @@ -74,108 +95,154 @@ class ESFtpClient extends ESObject { * @param arguments The argument list * @return true if successful, false otherwise */ - ESValue login(ESValue arguments[]) throws EcmaScriptException { - if (server == null) + ESValue login(ESValue[] arguments) throws EcmaScriptException { + if (server == null) { return ESBoolean.makeBoolean(false); + } + try { - ftpclient = new FTPClient (); - ftpclient.connect (server); - boolean b = ftpclient.login (arguments[0].toString(), arguments[1].toString()); - return ESBoolean.makeBoolean (b); + ftpclient = new FTPClient(); + ftpclient.connect(server); + + boolean b = ftpclient.login(arguments[0].toString(), arguments[1].toString()); + + return ESBoolean.makeBoolean(b); } catch (Exception x) { - return ESBoolean.makeBoolean (false); + return ESBoolean.makeBoolean(false); } catch (NoClassDefFoundError x) { - return ESBoolean.makeBoolean (false); + return ESBoolean.makeBoolean(false); } } - ESValue cd (ESValue arguments[]) throws EcmaScriptException { - if (ftpclient == null) - return ESBoolean.makeBoolean(false); + ESValue cd(ESValue[] arguments) throws EcmaScriptException { + if (ftpclient == null) { + return ESBoolean.makeBoolean(false); + } + try { - ftpclient.changeWorkingDirectory (arguments[0].toString ()); - return ESBoolean.makeBoolean(true); - } catch (Exception wrong) {} + ftpclient.changeWorkingDirectory(arguments[0].toString()); + + return ESBoolean.makeBoolean(true); + } catch (Exception wrong) { + } + return ESBoolean.makeBoolean(false); } + ESValue mkdir(ESValue[] arguments) throws EcmaScriptException { + if (ftpclient == null) { + return ESBoolean.makeBoolean(false); + } - ESValue mkdir (ESValue arguments[]) throws EcmaScriptException { - if (ftpclient == null) - return ESBoolean.makeBoolean(false); try { - return ESBoolean.makeBoolean(ftpclient.makeDirectory (arguments[0].toString ())); - } catch (Exception wrong) {} + return ESBoolean.makeBoolean(ftpclient.makeDirectory(arguments[0].toString())); + } catch (Exception wrong) { + } + return ESBoolean.makeBoolean(false); } - ESValue lcd (ESValue arguments[]) throws EcmaScriptException { + ESValue lcd(ESValue[] arguments) throws EcmaScriptException { try { - localDir = new File (arguments[0].toString()); - if (!localDir.exists()) - localDir.mkdirs(); - return ESBoolean.makeBoolean(true); - } catch (Exception wrong) {} + localDir = new File(arguments[0].toString()); + + if (!localDir.exists()) { + localDir.mkdirs(); + } + + return ESBoolean.makeBoolean(true); + } catch (Exception wrong) { + } + return ESBoolean.makeBoolean(false); } - ESValue putFile(ESValue arguments[]) throws EcmaScriptException { - if (ftpclient == null) - return ESBoolean.makeBoolean(false); + ESValue putFile(ESValue[] arguments) throws EcmaScriptException { + if (ftpclient == null) { + return ESBoolean.makeBoolean(false); + } + try { - String fn = arguments[0].toString(); - File f = localDir == null ? new File (fn) : new File (localDir, fn); - InputStream fin = new BufferedInputStream (new FileInputStream (f)); - ftpclient.storeFile (arguments[1].toString (), fin); - fin.close (); - return ESBoolean.makeBoolean(true); - } catch (Exception wrong) {} + String fn = arguments[0].toString(); + File f = (localDir == null) ? new File(fn) : new File(localDir, fn); + InputStream fin = new BufferedInputStream(new FileInputStream(f)); + + ftpclient.storeFile(arguments[1].toString(), fin); + fin.close(); + + return ESBoolean.makeBoolean(true); + } catch (Exception wrong) { + } + return ESBoolean.makeBoolean(false); } - ESValue putString(ESValue arguments[]) throws EcmaScriptException { - if (ftpclient == null) - return ESBoolean.makeBoolean(false); + ESValue putString(ESValue[] arguments) throws EcmaScriptException { + if (ftpclient == null) { + return ESBoolean.makeBoolean(false); + } + try { - byte[] bytes = null; - // check if this already is a byte array - if (arguments[0] instanceof ESArrayWrapper) { - Object o = ((ESArrayWrapper) arguments[0]).toJavaObject (); - if (o instanceof byte[]) - bytes = (byte[]) o; - } - if (bytes == null) - bytes = arguments[0].toString().getBytes(); - ByteArrayInputStream bin = new ByteArrayInputStream (bytes); - ftpclient.storeFile (arguments[1].toString (), bin); - return ESBoolean.makeBoolean(true); - } catch (Exception wrong) {} + byte[] bytes = null; + + // check if this already is a byte array + if (arguments[0] instanceof ESArrayWrapper) { + Object o = ((ESArrayWrapper) arguments[0]).toJavaObject(); + + if (o instanceof byte[]) { + bytes = (byte[]) o; + } + } + + if (bytes == null) { + bytes = arguments[0].toString().getBytes(); + } + + ByteArrayInputStream bin = new ByteArrayInputStream(bytes); + + ftpclient.storeFile(arguments[1].toString(), bin); + + return ESBoolean.makeBoolean(true); + } catch (Exception wrong) { + } + return ESBoolean.makeBoolean(false); } + ESValue getFile(ESValue[] arguments) throws EcmaScriptException { + if (ftpclient == null) { + return ESBoolean.makeBoolean(false); + } - ESValue getFile(ESValue arguments[]) throws EcmaScriptException { - if (ftpclient == null ) - return ESBoolean.makeBoolean(false); try { - String fn = arguments[0].toString(); - File f = localDir == null ? new File (fn) : new File (localDir, fn); - OutputStream out = new BufferedOutputStream (new FileOutputStream(f)); - ftpclient.retrieveFile (arguments[0].toString (), out); - out.close (); - return ESBoolean.makeBoolean(true); - } catch (Exception wrong) {} + String fn = arguments[0].toString(); + File f = (localDir == null) ? new File(fn) : new File(localDir, fn); + OutputStream out = new BufferedOutputStream(new FileOutputStream(f)); + + ftpclient.retrieveFile(arguments[0].toString(), out); + out.close(); + + return ESBoolean.makeBoolean(true); + } catch (Exception wrong) { + } + return ESBoolean.makeBoolean(false); } - ESValue getString(ESValue arguments[]) throws EcmaScriptException { - if (ftpclient == null ) + ESValue getString(ESValue[] arguments) throws EcmaScriptException { + if (ftpclient == null) { return ESNull.theNull; + } + try { - ByteArrayOutputStream bout = new ByteArrayOutputStream (); - ftpclient.retrieveFile (arguments[0].toString (), bout); - return new ESString (bout.toString ()); - } catch (Exception wrong) {} + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + + ftpclient.retrieveFile(arguments[0].toString(), bout); + + return new ESString(bout.toString()); + } catch (Exception wrong) { + } + return ESNull.theNull; } @@ -185,84 +252,144 @@ class ESFtpClient extends ESObject { * @param arguments The argument list * @return true if successful, false otherwise */ - ESValue logout (ESValue arguments[]) throws EcmaScriptException { + ESValue logout(ESValue[] arguments) throws EcmaScriptException { if (ftpclient != null) { - try { - ftpclient.logout (); - } catch (IOException ignore) {} - try { - ftpclient.disconnect (); - } catch (IOException ignore) {} + try { + ftpclient.logout(); + } catch (IOException ignore) { + } + + try { + ftpclient.disconnect(); + } catch (IOException ignore) { + } } - return ESBoolean.makeBoolean (true); + + return ESBoolean.makeBoolean(true); } - ESValue binary (ESValue arguments[]) throws EcmaScriptException { + ESValue binary(ESValue[] arguments) throws EcmaScriptException { if (ftpclient != null) { - try { - ftpclient.setFileType (FTP.BINARY_FILE_TYPE); - return ESBoolean.makeBoolean (true); - } catch (IOException ignore) {} + try { + ftpclient.setFileType(FTP.BINARY_FILE_TYPE); + + return ESBoolean.makeBoolean(true); + } catch (IOException ignore) { + } } - return ESBoolean.makeBoolean (false); + + return ESBoolean.makeBoolean(false); } - ESValue ascii (ESValue arguments[]) throws EcmaScriptException { + ESValue ascii(ESValue[] arguments) throws EcmaScriptException { if (ftpclient != null) { - try { - ftpclient.setFileType (FTP.ASCII_FILE_TYPE); - return ESBoolean.makeBoolean (true); - } catch (IOException ignore) {} + try { + ftpclient.setFileType(FTP.ASCII_FILE_TYPE); + + return ESBoolean.makeBoolean(true); + } catch (IOException ignore) { + } } - return ESBoolean.makeBoolean (false); + + return ESBoolean.makeBoolean(false); } - - } +/** + * + */ public class FtpExtension extends Extension { - private transient Evaluator evaluator = null; private ESObject esFtpPrototype = null; - public FtpExtension () { + /** + * Creates a new FtpExtension object. + */ + public FtpExtension() { super(); } + /** + * + * + * @param evaluator ... + * + * @throws EcmaScriptException ... + */ + public void initializeExtension(Evaluator evaluator) + throws EcmaScriptException { + this.evaluator = evaluator; + + GlobalObject go = evaluator.getGlobalObject(); + ObjectPrototype op = (ObjectPrototype) evaluator.getObjectPrototype(); + FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); + + esFtpPrototype = new ESFtpClient(op, evaluator); + + ESObject globalFtpObject = new GlobalObjectFtpClient("FtpClient", evaluator, fp); + + globalFtpObject.putHiddenProperty("prototype", esFtpPrototype); + globalFtpObject.putHiddenProperty("length", new ESNumber(1)); + + esFtpPrototype.putHiddenProperty("login", + new FtpClientLogin("login", evaluator, fp)); + esFtpPrototype.putHiddenProperty("cd", new FtpClientCD("cd", evaluator, fp)); + esFtpPrototype.putHiddenProperty("mkdir", + new FtpClientMKDIR("mkdir", evaluator, fp)); + esFtpPrototype.putHiddenProperty("lcd", new FtpClientLCD("lcd", evaluator, fp)); + esFtpPrototype.putHiddenProperty("putFile", + new FtpClientPutFile("putFile", evaluator, fp)); + esFtpPrototype.putHiddenProperty("putString", + new FtpClientPutString("putString", evaluator, fp)); + esFtpPrototype.putHiddenProperty("getFile", + new FtpClientGetFile("getFile", evaluator, fp)); + esFtpPrototype.putHiddenProperty("getString", + new FtpClientGetString("getString", evaluator, fp)); + esFtpPrototype.putHiddenProperty("logout", + new FtpClientLogout("logout", evaluator, fp)); + esFtpPrototype.putHiddenProperty("binary", + new FtpClientBinary("binary", evaluator, fp)); + esFtpPrototype.putHiddenProperty("ascii", + new FtpClientAscii("ascii", evaluator, fp)); + + go.putHiddenProperty("FtpClient", globalFtpObject); + } + class GlobalObjectFtpClient extends BuiltinFunctionObject { GlobalObjectFtpClient(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } public ESValue callFunction(ESObject thisObject, ESValue[] arguments) - throws EcmaScriptException { - return doConstruct(thisObject, arguments); + throws EcmaScriptException { + return doConstruct(thisObject, arguments); } public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = null; - if (arguments.length != 1) - throw new EcmaScriptException("FtpClient requires 1 argument"); - ftp = new ESFtpClient (esFtpPrototype, - this.evaluator, - arguments[0]); - return ftp; + throws EcmaScriptException { + ESFtpClient ftp = null; + + if (arguments.length != 1) { + throw new EcmaScriptException("FtpClient requires 1 argument"); + } + + ftp = new ESFtpClient(esFtpPrototype, this.evaluator, arguments[0]); + + return ftp; } - } - class FtpClientLogin extends BuiltinFunctionObject { FtpClientLogin(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.login (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.login(arguments); } } @@ -270,11 +397,12 @@ public class FtpExtension extends Extension { FtpClientCD(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.cd (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.cd(arguments); } } @@ -282,11 +410,12 @@ public class FtpExtension extends Extension { FtpClientMKDIR(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.mkdir (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.mkdir(arguments); } } @@ -294,59 +423,64 @@ public class FtpExtension extends Extension { FtpClientLCD(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.lcd (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.lcd(arguments); } } class FtpClientPutFile extends BuiltinFunctionObject { - FtpClientPutFile (String name, Evaluator evaluator, FunctionPrototype fp) { + FtpClientPutFile(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.putFile (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.putFile(arguments); } } class FtpClientPutString extends BuiltinFunctionObject { - FtpClientPutString (String name, Evaluator evaluator, FunctionPrototype fp) { + FtpClientPutString(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.putString (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.putString(arguments); } } class FtpClientGetFile extends BuiltinFunctionObject { - FtpClientGetFile (String name, Evaluator evaluator, FunctionPrototype fp) { + FtpClientGetFile(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.getFile (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.getFile(arguments); } } class FtpClientGetString extends BuiltinFunctionObject { - FtpClientGetString (String name, Evaluator evaluator, FunctionPrototype fp) { + FtpClientGetString(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.getString (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.getString(arguments); } } @@ -354,11 +488,12 @@ public class FtpExtension extends Extension { FtpClientLogout(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.logout (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.logout(arguments); } } @@ -366,11 +501,12 @@ public class FtpExtension extends Extension { FtpClientBinary(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.binary (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.binary(arguments); } } @@ -378,42 +514,12 @@ public class FtpExtension extends Extension { FtpClientAscii(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - public ESValue callFunction(ESObject thisObject, - ESValue[] arguments) - throws EcmaScriptException { - ESFtpClient ftp = (ESFtpClient) thisObject; - return ftp.ascii (arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESFtpClient ftp = (ESFtpClient) thisObject; + + return ftp.ascii(arguments); } } - - - public void initializeExtension(Evaluator evaluator) throws EcmaScriptException { - - this.evaluator = evaluator; - GlobalObject go = evaluator.getGlobalObject(); - ObjectPrototype op = (ObjectPrototype) evaluator.getObjectPrototype(); - FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); - - esFtpPrototype = new ESFtpClient (op, evaluator); - - ESObject globalFtpObject = new GlobalObjectFtpClient("FtpClient", evaluator, fp); - globalFtpObject.putHiddenProperty("prototype",esFtpPrototype); - globalFtpObject.putHiddenProperty("length",new ESNumber(1)); - - - esFtpPrototype.putHiddenProperty("login", new FtpClientLogin("login", evaluator, fp)); - esFtpPrototype.putHiddenProperty("cd", new FtpClientCD("cd", evaluator, fp)); - esFtpPrototype.putHiddenProperty("mkdir", new FtpClientMKDIR("mkdir", evaluator, fp)); - esFtpPrototype.putHiddenProperty("lcd", new FtpClientLCD("lcd", evaluator, fp)); - esFtpPrototype.putHiddenProperty("putFile", new FtpClientPutFile("putFile", evaluator, fp)); - esFtpPrototype.putHiddenProperty("putString", new FtpClientPutString("putString", evaluator, fp)); - esFtpPrototype.putHiddenProperty("getFile", new FtpClientGetFile("getFile", evaluator, fp)); - esFtpPrototype.putHiddenProperty("getString", new FtpClientGetString("getString", evaluator, fp)); - esFtpPrototype.putHiddenProperty("logout", new FtpClientLogout("logout", evaluator, fp)); - esFtpPrototype.putHiddenProperty("binary", new FtpClientBinary("binary", evaluator, fp)); - esFtpPrototype.putHiddenProperty("ascii", new FtpClientAscii("ascii", evaluator, fp)); - - go.putHiddenProperty("FtpClient", globalFtpObject); - - } - } +} diff --git a/src/helma/scripting/fesi/extensions/ImageExtension.java b/src/helma/scripting/fesi/extensions/ImageExtension.java index 2add810d..822e38a4 100644 --- a/src/helma/scripting/fesi/extensions/ImageExtension.java +++ b/src/helma/scripting/fesi/extensions/ImageExtension.java @@ -1,106 +1,124 @@ -// ImageExtension.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * 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.scripting.fesi.extensions; -import helma.objectmodel.*; -import helma.util.*; -import helma.image.*; - -import FESI.Interpreter.*; +import FESI.Data.*; import FESI.Exceptions.*; import FESI.Extensions.*; -import FESI.Data.*; - -import java.io.*; +import FESI.Interpreter.*; +import helma.image.*; +import helma.objectmodel.*; +import helma.util.*; import java.awt.image.*; -import java.util.*; +import java.io.*; import java.rmi.Naming; - +import java.util.*; /** * Extension to do Image manipulation from HOP. */ - public class ImageExtension extends Extension { - + static ImageGenerator imggen; protected Evaluator evaluator = null; - static ImageGenerator imggen; - - - public ImageExtension () { + /** + * Creates a new ImageExtension object. + */ + public ImageExtension() { super(); } + /** + * Called by the evaluator after the extension is loaded. + */ + public void initializeExtension(Evaluator evaluator) + throws EcmaScriptException { + this.evaluator = evaluator; + + GlobalObject go = evaluator.getGlobalObject(); + FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); + ESObject image = new GlobalObjectImage("Image", evaluator, fp, this); // the Image constructor + + go.putHiddenProperty("Image", image); // register the constructor for a Image object. + } class GlobalObjectImage extends BuiltinFunctionObject { - ImageExtension imagex; - GlobalObjectImage (String name, Evaluator evaluator, FunctionPrototype fp, ImageExtension imagex) { + GlobalObjectImage(String name, Evaluator evaluator, FunctionPrototype fp, + ImageExtension imagex) { super(fp, evaluator, name, 1); this.imagex = imagex; } - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return doConstruct(thisObject, arguments); + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + return doConstruct(thisObject, arguments); } - public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { Object img = null; try { if (imggen == null) { try { - imggen = new ImageGenerator (); + imggen = new ImageGenerator(); } catch (UnsatisfiedLinkError noawt) { - System.err.println ("Error creating Image: "+noawt); + System.err.println("Error creating Image: " + noawt); } } if (arguments.length == 1) { if (arguments[0] instanceof ESArrayWrapper) { - Object obj = ((ESArrayWrapper) arguments[0]).toJavaObject (); + Object obj = ((ESArrayWrapper) arguments[0]).toJavaObject(); + if (obj instanceof byte[]) { - img = imggen.createImage ((byte[]) obj); + img = imggen.createImage((byte[]) obj); } } else if (arguments[0] instanceof ESString) { - String imgurl = arguments[0].toString (); - img = imggen.createPaintableImage (imgurl); + String imgurl = arguments[0].toString(); + + img = imggen.createPaintableImage(imgurl); } } else if (arguments.length == 2) { - if (arguments[0] instanceof ESWrapper && arguments[1] instanceof ESWrapper) { + if (arguments[0] instanceof ESWrapper && + arguments[1] instanceof ESWrapper) { // create a new image from an existing one and an image filter - Object image = arguments[0].toJavaObject (); - Object filter = arguments[1].toJavaObject (); - img = imggen.createPaintableImage ((ImageWrapper) image, (ImageFilter) filter); - } else if (arguments[0].isNumberValue () && arguments[1].isNumberValue ()) { - img = imggen.createPaintableImage (arguments[0].toInt32(), arguments[1].toInt32()); + Object image = arguments[0].toJavaObject(); + Object filter = arguments[1].toJavaObject(); + + img = imggen.createPaintableImage((ImageWrapper) image, + (ImageFilter) filter); + } else if (arguments[0].isNumberValue() && + arguments[1].isNumberValue()) { + img = imggen.createPaintableImage(arguments[0].toInt32(), + arguments[1].toInt32()); } } } catch (Exception error) { - System.err.println ("Error creating Image: "+error); + System.err.println("Error creating Image: " + error); } - if (img == null) - throw new EcmaScriptException ("Error creating image: Bad parameters or setup problem."); + if (img == null) { + throw new EcmaScriptException("Error creating image: Bad parameters or setup problem."); + } - return new ESWrapper (img, this.evaluator); + return new ESWrapper(img, this.evaluator); } } - - - /** - * Called by the evaluator after the extension is loaded. - */ - public void initializeExtension(Evaluator evaluator) throws EcmaScriptException { - this.evaluator = evaluator; - GlobalObject go = evaluator.getGlobalObject(); - FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); - ESObject image = new GlobalObjectImage ("Image", evaluator, fp, this); // the Image constructor - go.putHiddenProperty("Image", image); // register the constructor for a Image object. - } - } - diff --git a/src/helma/scripting/fesi/extensions/MailExtension.java b/src/helma/scripting/fesi/extensions/MailExtension.java index 103e0d4b..8b69f841 100644 --- a/src/helma/scripting/fesi/extensions/MailExtension.java +++ b/src/helma/scripting/fesi/extensions/MailExtension.java @@ -1,254 +1,324 @@ -// MailExtension.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * 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.scripting.fesi.extensions; -import helma.util.*; - -import FESI.Interpreter.*; +import FESI.Data.*; import FESI.Exceptions.*; import FESI.Extensions.*; -import FESI.Data.*; - +import FESI.Interpreter.*; +import helma.util.*; import java.io.*; import java.util.*; - -/** +/** * Extension to create and send mail messages via SMTP from HOP applications */ - public class MailExtension extends Extension { - - protected Evaluator eval = null; protected ObjectPrototype esMailPrototype = null; protected Properties mprops; - - public MailExtension () { + /** + * Creates a new MailExtension object. + */ + public MailExtension() { super(); } - public void setProperties (Properties props) { + /** + * + * + * @param props ... + */ + public void setProperties(Properties props) { this.mprops = props; } /** * Called by the evaluator after the extension is loaded. */ - public void initializeExtension(Evaluator evaluator) throws EcmaScriptException { - + public void initializeExtension(Evaluator evaluator) + throws EcmaScriptException { this.eval = evaluator; + GlobalObject go = evaluator.getGlobalObject(); FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); ESObject op = evaluator.getObjectPrototype(); - esMailPrototype = new ObjectPrototype(op, evaluator); // the Node prototype - ESObject mail = new GlobalObjectMail ("Mail", evaluator, fp, this); // the Mail constructor + esMailPrototype = new ObjectPrototype(op, evaluator); // the Node prototype + + ESObject mail = new GlobalObjectMail("Mail", evaluator, fp, this); // the Mail constructor go.putHiddenProperty("Mail", mail); // register the constructor for a Mail object. - // methods for sending mail from JS... - ESObject p = new MailSetText ("setText", evaluator, fp); + ESObject p = new MailSetText("setText", evaluator, fp); + esMailPrototype.putHiddenProperty("setText", p); esMailPrototype.putHiddenProperty("addText", p); - esMailPrototype.putHiddenProperty("addPart", new MailAddPart ("addPart", evaluator, fp)); - esMailPrototype.putHiddenProperty("setSubject", new MailSetSubject ("setSubject", evaluator, fp)); - esMailPrototype.putHiddenProperty("setReplyTo", new MailSetReplyTo ("setReplyTo", evaluator, fp)); - esMailPrototype.putHiddenProperty("setFrom", new MailSetFrom ("setFrom", evaluator, fp)); + esMailPrototype.putHiddenProperty("addPart", + new MailAddPart("addPart", evaluator, fp)); + esMailPrototype.putHiddenProperty("setSubject", + new MailSetSubject("setSubject", evaluator, fp)); + esMailPrototype.putHiddenProperty("setReplyTo", + new MailSetReplyTo("setReplyTo", evaluator, fp)); + esMailPrototype.putHiddenProperty("setFrom", + new MailSetFrom("setFrom", evaluator, fp)); - p = new MailAddTo ("addTo", evaluator, fp); + p = new MailAddTo("addTo", evaluator, fp); esMailPrototype.putHiddenProperty("addTo", p); esMailPrototype.putHiddenProperty("setTo", p); - p = new MailAddCC ("addCC", evaluator, fp); + p = new MailAddCC("addCC", evaluator, fp); esMailPrototype.putHiddenProperty("addCC", p); esMailPrototype.putHiddenProperty("setCC", p); - p = new MailAddBCC ("addBCC", evaluator, fp); + p = new MailAddBCC("addBCC", evaluator, fp); esMailPrototype.putHiddenProperty("addBCC", p); esMailPrototype.putHiddenProperty("setBCC", p); - esMailPrototype.putHiddenProperty("send", new MailSend ("send", evaluator, fp)); - + esMailPrototype.putHiddenProperty("send", new MailSend("send", evaluator, fp)); } - class GlobalObjectMail extends BuiltinFunctionObject { - MailExtension mailx; - GlobalObjectMail (String name, Evaluator evaluator, FunctionPrototype fp, MailExtension mailx) { + GlobalObjectMail(String name, Evaluator evaluator, FunctionPrototype fp, + MailExtension mailx) { super(fp, evaluator, name, 1); this.mailx = mailx; } - - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return doConstruct(thisObject, arguments); - } - - public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESMail mail = null; - if (arguments.length == 0) { - mail = new ESMail (mailx); - } else { - mail = new ESMail (mailx); - // should/could do something with extra arguments... - } - return mail; + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + return doConstruct(thisObject, arguments); + } + + public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESMail mail = null; + + if (arguments.length == 0) { + mail = new ESMail(mailx); + } else { + mail = new ESMail(mailx); + + // should/could do something with extra arguments... + } + + return mail; } } - - + class MailSetText extends BuiltinFunctionObject { - MailSetText (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + MailSetText(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { ESMail mail = (ESMail) thisObject; - if (arguments.length == 1) try { - mail.setText (arguments[0]); - } catch (Exception x) { - mail.setStatus (ESMail.TEXT); - return ESBoolean.makeBoolean(false); + + if (arguments.length == 1) { + try { + mail.setText(arguments[0]); + } catch (Exception x) { + mail.setStatus(ESMail.TEXT); + + return ESBoolean.makeBoolean(false); + } } + return ESBoolean.makeBoolean(true); } } class MailAddPart extends BuiltinFunctionObject { - MailAddPart (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + MailAddPart(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { ESMail mail = (ESMail) thisObject; + try { - mail.addPart (arguments); + mail.addPart(arguments); } catch (Exception x) { - mail.setStatus (ESMail.MIMEPART); - return ESBoolean.makeBoolean(false); - } - return ESBoolean.makeBoolean(true); - } - } - - class MailSetSubject extends BuiltinFunctionObject { - MailSetSubject (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESMail mail = (ESMail) thisObject; - if (arguments.length == 1) try { - mail.setSubject (arguments[0]); - } catch (Exception x) { - mail.setStatus (ESMail.SUBJECT); + mail.setStatus(ESMail.MIMEPART); + return ESBoolean.makeBoolean(false); } + return ESBoolean.makeBoolean(true); } } - - class MailSetReplyTo extends BuiltinFunctionObject { - MailSetReplyTo (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + + class MailSetSubject extends BuiltinFunctionObject { + MailSetSubject(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { ESMail mail = (ESMail) thisObject; - if (arguments.length == 1) try { - mail.setReplyTo (arguments[0]); - } catch (Exception x) { - mail.setStatus (ESMail.REPLYTO); - return ESBoolean.makeBoolean(false); + + if (arguments.length == 1) { + try { + mail.setSubject(arguments[0]); + } catch (Exception x) { + mail.setStatus(ESMail.SUBJECT); + + return ESBoolean.makeBoolean(false); + } } + + return ESBoolean.makeBoolean(true); + } + } + + class MailSetReplyTo extends BuiltinFunctionObject { + MailSetReplyTo(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESMail mail = (ESMail) thisObject; + + if (arguments.length == 1) { + try { + mail.setReplyTo(arguments[0]); + } catch (Exception x) { + mail.setStatus(ESMail.REPLYTO); + + return ESBoolean.makeBoolean(false); + } + } + return ESBoolean.makeBoolean(true); } } class MailSetFrom extends BuiltinFunctionObject { - MailSetFrom (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); + MailSetFrom(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESMail mail = (ESMail) thisObject; - try { - mail.setFrom (arguments); - } catch (Exception x) { - mail.setStatus (ESMail.FROM); - return ESBoolean.makeBoolean(false); - } - return ESBoolean.makeBoolean(true); - } - } - class MailAddTo extends BuiltinFunctionObject { - MailAddTo (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { ESMail mail = (ESMail) thisObject; - try { - mail.addTo (arguments); - } catch (Exception x) { - mail.setStatus (ESMail.TO); - return ESBoolean.makeBoolean(false); - } - return ESBoolean.makeBoolean(true); - } - } - class MailAddCC extends BuiltinFunctionObject { - MailAddCC (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESMail mail = (ESMail) thisObject; try { - mail.addCC (arguments); + mail.setFrom(arguments); } catch (Exception x) { - mail.setStatus (ESMail.CC); - return ESBoolean.makeBoolean(false); - } - return ESBoolean.makeBoolean(true); - } - } - - class MailAddBCC extends BuiltinFunctionObject { - MailAddBCC (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESMail mail = (ESMail) thisObject; - try { - mail.addBCC (arguments); - } catch (Exception x) { - mail.setStatus (ESMail.BCC); - return ESBoolean.makeBoolean(false); - } - return ESBoolean.makeBoolean(true); - } - } + mail.setStatus(ESMail.FROM); - class MailSend extends BuiltinFunctionObject { - MailSend (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESMail mail = (ESMail) thisObject; - try { - mail.send (); - } catch (Exception x) { - evaluator.engine.getApplication().logEvent ("Error sending mail: "+x); - mail.setStatus (ESMail.SEND); return ESBoolean.makeBoolean(false); } + return ESBoolean.makeBoolean(true); } } -} + class MailAddTo extends BuiltinFunctionObject { + MailAddTo(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESMail mail = (ESMail) thisObject; + + try { + mail.addTo(arguments); + } catch (Exception x) { + mail.setStatus(ESMail.TO); + + return ESBoolean.makeBoolean(false); + } + + return ESBoolean.makeBoolean(true); + } + } + + class MailAddCC extends BuiltinFunctionObject { + MailAddCC(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESMail mail = (ESMail) thisObject; + + try { + mail.addCC(arguments); + } catch (Exception x) { + mail.setStatus(ESMail.CC); + + return ESBoolean.makeBoolean(false); + } + + return ESBoolean.makeBoolean(true); + } + } + + class MailAddBCC extends BuiltinFunctionObject { + MailAddBCC(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESMail mail = (ESMail) thisObject; + + try { + mail.addBCC(arguments); + } catch (Exception x) { + mail.setStatus(ESMail.BCC); + + return ESBoolean.makeBoolean(false); + } + + return ESBoolean.makeBoolean(true); + } + } + + class MailSend extends BuiltinFunctionObject { + MailSend(String name, Evaluator evaluator, FunctionPrototype fp) { + super(fp, evaluator, name, 1); + } + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESMail mail = (ESMail) thisObject; + + try { + mail.send(); + } catch (Exception x) { + evaluator.engine.getApplication().logEvent("Error sending mail: " + x); + mail.setStatus(ESMail.SEND); + + return ESBoolean.makeBoolean(false); + } + + return ESBoolean.makeBoolean(true); + } + } +} diff --git a/src/helma/scripting/fesi/extensions/XmlRpcExtension.java b/src/helma/scripting/fesi/extensions/XmlRpcExtension.java index 40cd0887..dbc9bfe5 100644 --- a/src/helma/scripting/fesi/extensions/XmlRpcExtension.java +++ b/src/helma/scripting/fesi/extensions/XmlRpcExtension.java @@ -1,140 +1,182 @@ -// XmlRpcExtension.java -// Copyright (c) Hannes Wallnöfer, 1999 - All rights reserved +/* + * 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.scripting.fesi.extensions; -import org.apache.xmlrpc.*; - -import helma.scripting.fesi.FesiEngine; - -import FESI.Interpreter.*; +import FESI.Data.*; import FESI.Exceptions.*; import FESI.Extensions.*; -import FESI.Data.*; - +import FESI.Interpreter.*; +import helma.scripting.fesi.FesiEngine; +import org.apache.xmlrpc.*; import java.io.*; -import java.util.*; import java.net.*; +import java.util.*; - -/** - * An extension to transparently call and serve XML-RPC from the - * FESI EcmaScript interpreter. - * The extension adds constructors for XML-RPC clients and servers to the Global Object. - * For more information on how to use this please look at the files server.es and +/** + * An extension to transparently call and serve XML-RPC from the + * FESI EcmaScript interpreter. + * The extension adds constructors for XML-RPC clients and servers to the Global Object. + * For more information on how to use this please look at the files server.es and * client.es in the src/fesi directory of the distribution. - * - * All argument conversion is done automatically. Currently the following argument and return + * + * All argument conversion is done automatically. Currently the following argument and return * types are supported: *
    *
  • plain objects (with all properties returned by ESObject.getProperties ()) - *
  • arrays + *
  • arrays *
  • strings *
  • date objects *
  • booleans *
  • integer and float numbers (long values are not supported!) *
- * + * */ public class XmlRpcExtension extends Extension { - Evaluator evaluator; ESObject op; - public void initializeExtension (Evaluator evaluator) throws EcmaScriptException { + /** + * + * + * @param evaluator ... + * + * @throws EcmaScriptException ... + */ + public void initializeExtension(Evaluator evaluator) + throws EcmaScriptException { // XmlRpc.setDebug (true); this.evaluator = evaluator; + GlobalObject go = evaluator.getGlobalObject(); FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); op = evaluator.getObjectPrototype(); - go.putHiddenProperty ("Remote", new GlobalObjectRemote ("Remote", evaluator, fp)); // the Remote constructor + go.putHiddenProperty("Remote", new GlobalObjectRemote("Remote", evaluator, fp)); // the Remote constructor } - class GlobalObjectRemote extends BuiltinFunctionObject { - - GlobalObjectRemote (String name, Evaluator evaluator, FunctionPrototype fp) { + GlobalObjectRemote(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); } - - public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return doConstruct(thisObject, arguments); + + public ESValue callFunction(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + return doConstruct(thisObject, arguments); } - - public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - ESObject remote = null; - String url = null; - String robj = null; - if (arguments.length >= 1) - url = arguments[0].toString (); - if (arguments.length >= 2) - robj = arguments[1].toString (); - try { - remote = new ESRemote (op, this.evaluator, url, robj); - } catch (MalformedURLException x) { - throw new EcmaScriptException (x.toString ()); - } - return remote; + + public ESObject doConstruct(ESObject thisObject, ESValue[] arguments) + throws EcmaScriptException { + ESObject remote = null; + String url = null; + String robj = null; + + if (arguments.length >= 1) { + url = arguments[0].toString(); + } + + if (arguments.length >= 2) { + robj = arguments[1].toString(); + } + + try { + remote = new ESRemote(op, this.evaluator, url, robj); + } catch (MalformedURLException x) { + throw new EcmaScriptException(x.toString()); + } + + return remote; } } - class ESRemote extends ObjectPrototype { - URL url; String remoteObject; - - public ESRemote (ESObject prototype, Evaluator evaluator, String urlstring, String robj) throws MalformedURLException { - super (prototype, evaluator); - this.url = new URL (urlstring); + + public ESRemote(ESObject prototype, Evaluator evaluator, String urlstring, + String robj) throws MalformedURLException { + super(prototype, evaluator); + this.url = new URL(urlstring); remoteObject = robj; } - public ESRemote (ESObject prototype, Evaluator evaluator, URL url, String robj) { - super (prototype, evaluator); + public ESRemote(ESObject prototype, Evaluator evaluator, URL url, String robj) { + super(prototype, evaluator); this.url = url; remoteObject = robj; } - - public ESValue doIndirectCall(Evaluator evaluator, ESObject target, String functionName, ESValue arguments[]) - throws EcmaScriptException, NoSuchMethodException { + + public ESValue doIndirectCall(Evaluator evaluator, ESObject target, + String functionName, ESValue[] arguments) + throws EcmaScriptException, NoSuchMethodException { // System.out.println ("doIndirectCall called with "+remoteObject+"."+functionName); - XmlRpcClient client = new XmlRpcClient (url); + XmlRpcClient client = new XmlRpcClient(url); + // long now = System.currentTimeMillis (); Object retval = null; int l = arguments.length; - Vector v = new Vector (); - for (int i=0; i 0) { - reqtrans.set (key, values[0]); // set to single string value - if (values.length > 1) - reqtrans.set (key+"_array", values); // set string array - } - } - - // check for MIME file uploads - String contentType = request.getContentType(); - if (contentType != null && contentType.indexOf("multipart/form-data")==0) { - // File Upload - try { - FileUpload upload = getUpload (request); - if (upload != null) { - Hashtable parts = upload.getParts (); - for (Enumeration e = parts.keys(); e.hasMoreElements(); ) { - String nextKey = (String) e.nextElement (); - Object nextPart = parts.get (nextKey); - reqtrans.set (nextKey, nextPart); - } - } - } catch (Exception upx) { - sendError ( - response, - response.SC_REQUEST_ENTITY_TOO_LARGE, - "Sorry, upload size exceeds limit of "+uploadLimit+"kB."); - return; - } - } - - // read cookies - Cookie[] reqCookies = request.getCookies(); - if (reqCookies != null) { - for (int i=0; i < reqCookies.length;i++) try { - // get Cookies - String nextKey = reqCookies[i].getName (); - String nextPart = reqCookies[i].getValue (); - if ("HopSession".equals (nextKey)) - reqtrans.session = nextPart; - else - reqtrans.set (nextKey, nextPart); - } catch (Exception badCookie) {} - } - - // do standard HTTP variables - String host = request.getHeader ("Host"); - if (host != null) { - host = host.toLowerCase(); - reqtrans.set ("http_host", host); - } - - String referer = request.getHeader ("Referer"); - if (referer != null) - reqtrans.set ("http_referer", referer); - - try { - long ifModifiedSince = request.getDateHeader ("If-Modified-Since"); - if (ifModifiedSince > -1) - reqtrans.setIfModifiedSince (ifModifiedSince); - } catch (IllegalArgumentException ignore) {} - - String ifNoneMatch = request.getHeader ("If-None-Match"); - if (ifNoneMatch != null) - reqtrans.setETags (ifNoneMatch); - - String remotehost = request.getRemoteAddr (); - if (remotehost != null) - reqtrans.set ("http_remotehost", remotehost); - - // get the cookie domain to use for this response, if any. - String resCookieDomain = cookieDomain; - if (resCookieDomain != null) { - // check if cookieDomain is valid for this response. - // (note: cookieDomain is guaranteed to be lower case) - if (host != null && host.toLowerCase().indexOf (cookieDomain) == -1) - resCookieDomain = null; - } - // check if we need to create a session id. also handle the - // case that the session id doesn't match the remote host address - if (reqtrans.session == null || !reqtrans.session.startsWith (remotehost)) { - reqtrans.session = remotehost+"."+Long.toString ( - Math.round (Math.random ()* Long.MAX_VALUE) - - System.currentTimeMillis (), 36); - Cookie c = new Cookie("HopSession", reqtrans.session); - c.setPath ("/"); - if (resCookieDomain != null) - c.setDomain (resCookieDomain); - response.addCookie(c); - } - - String browser = request.getHeader ("User-Agent"); - if (browser != null) - reqtrans.set ("http_browser", browser); - - String authorization = request.getHeader("authorization"); - if ( authorization != null ) - reqtrans.set ("authorization", authorization ); - - // response.setHeader ("Server", "Helma/"+helma.main.Server.version); - - reqtrans.path = getPathInfo (request); - ResponseTrans restrans = execute (reqtrans); - - // set cookies - int ncookies = restrans.countCookies(); - if (restrans.countCookies() > 0) { - CookieTrans[] resCookies = restrans.getCookies (); - for (int i = 0; i < resCookies.length; i++) try { - Cookie c = resCookies[i].getCookie ("/", resCookieDomain); - response.addCookie(c); - } catch (Exception ignore) {} - } - // write response - writeResponse (request, response, restrans); - - } catch (Exception x) { - try { - if (debug) - sendError ( - response, - response.SC_INTERNAL_SERVER_ERROR, - "Error in request handler:" +x); - else - sendError ( - response, - response.SC_INTERNAL_SERVER_ERROR, - "The server encountered an error while processing your request. "+ - "Please check back later."); - log ("Exception in execute: "+x); - } catch (IOException io_e) { - log ("Exception in sendError: "+io_e); - } - } + /** + * + * + * @param request ... + * @param response ... + * + * @throws ServletException ... + * @throws IOException ... + */ + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + execute(request, response, HTTP_POST); } + protected void execute(HttpServletRequest request, HttpServletResponse response, + byte method) { + RequestTrans reqtrans = new RequestTrans(method); - void writeResponse (HttpServletRequest req, - HttpServletResponse res, - ResponseTrans hopres) { + // get app and path from original request path + // String pathInfo = request.getPathInfo (); + // String appID = getAppID (pathInfo); + // reqtrans.path = getRequestPath (pathInfo); + try { + // read and set http parameters + Map parameters = parseParameters(request); - if (hopres.getETag() != null) { - res.setHeader ("ETag", hopres.getETag()); - } - if (hopres.getRedirect () != null) { - sendRedirect(req, res, hopres.getRedirect ()); - } else if (hopres.getNotModified ()) { - res.setStatus (HttpServletResponse.SC_NOT_MODIFIED); - } else { + for (Iterator i = parameters.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry) i.next(); + String key = (String) entry.getKey(); + String[] values = (String[]) entry.getValue(); - if (!hopres.cache || ! caching) { - // Disable caching of response. - // for HTTP 1.0 - res.setDateHeader ("Expires", System.currentTimeMillis ()-10000); - res.setHeader ("Pragma", "no-cache"); - // for HTTP 1.1 - res.setHeader ("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0"); - } - if ( hopres.realm!=null ) - res.setHeader( "WWW-Authenticate", "Basic realm=\"" + hopres.realm + "\"" ); - if (hopres.status > 0) - res.setStatus (hopres.status); - // set last-modified header to now - long modified = hopres.getLastModified (); - if (modified > -1) - res.setDateHeader ("Last-Modified", System.currentTimeMillis ()); - // if we don't know which charset to use for parsing HTTP params, - // take the one from the response. This usually works because - // browsers send parrameters in the same encoding as the page - // containing the form has. Problem is we can do this only per servlet, - // not per session or even per page, which would produce too much overhead - if (defaultEncoding == null) - defaultEncoding = hopres.charset; - res.setContentLength (hopres.getContentLength ()); - res.setContentType (hopres.getContentType ()); - try { - OutputStream out = res.getOutputStream (); - out.write (hopres.getContent ()); - out.flush (); - } catch(Exception io_e) { - log ("Exception in writeResponse: "+io_e); - } - } + if ((values != null) && (values.length > 0)) { + reqtrans.set(key, values[0]); // set to single string value + + if (values.length > 1) { + reqtrans.set(key + "_array", values); // set string array + } + } + } + + // check for MIME file uploads + String contentType = request.getContentType(); + + if ((contentType != null) && + (contentType.indexOf("multipart/form-data") == 0)) { + // File Upload + try { + FileUpload upload = getUpload(request); + + if (upload != null) { + Hashtable parts = upload.getParts(); + + for (Enumeration e = parts.keys(); e.hasMoreElements();) { + String nextKey = (String) e.nextElement(); + Object nextPart = parts.get(nextKey); + + reqtrans.set(nextKey, nextPart); + } + } + } catch (Exception upx) { + sendError(response, response.SC_REQUEST_ENTITY_TOO_LARGE, + "Sorry, upload size exceeds limit of " + uploadLimit + + "kB."); + + return; + } + } + + // read cookies + Cookie[] reqCookies = request.getCookies(); + + if (reqCookies != null) { + for (int i = 0; i < reqCookies.length; i++) + try { + // get Cookies + String nextKey = reqCookies[i].getName(); + String nextPart = reqCookies[i].getValue(); + + if ("HopSession".equals(nextKey)) { + reqtrans.session = nextPart; + } else { + reqtrans.set(nextKey, nextPart); + } + } catch (Exception badCookie) { + } + } + + // do standard HTTP variables + String host = request.getHeader("Host"); + + if (host != null) { + host = host.toLowerCase(); + reqtrans.set("http_host", host); + } + + String referer = request.getHeader("Referer"); + + if (referer != null) { + reqtrans.set("http_referer", referer); + } + + try { + long ifModifiedSince = request.getDateHeader("If-Modified-Since"); + + if (ifModifiedSince > -1) { + reqtrans.setIfModifiedSince(ifModifiedSince); + } + } catch (IllegalArgumentException ignore) { + } + + String ifNoneMatch = request.getHeader("If-None-Match"); + + if (ifNoneMatch != null) { + reqtrans.setETags(ifNoneMatch); + } + + String remotehost = request.getRemoteAddr(); + + if (remotehost != null) { + reqtrans.set("http_remotehost", remotehost); + } + + // get the cookie domain to use for this response, if any. + String resCookieDomain = cookieDomain; + + if (resCookieDomain != null) { + // check if cookieDomain is valid for this response. + // (note: cookieDomain is guaranteed to be lower case) + if ((host != null) && (host.toLowerCase().indexOf(cookieDomain) == -1)) { + resCookieDomain = null; + } + } + + // check if we need to create a session id. also handle the + // case that the session id doesn't match the remote host address + if ((reqtrans.session == null) || !reqtrans.session.startsWith(remotehost)) { + reqtrans.session = remotehost + "." + + Long.toString(Math.round(Math.random() * Long.MAX_VALUE) - + System.currentTimeMillis(), 36); + + Cookie c = new Cookie("HopSession", reqtrans.session); + + c.setPath("/"); + + if (resCookieDomain != null) { + c.setDomain(resCookieDomain); + } + + response.addCookie(c); + } + + String browser = request.getHeader("User-Agent"); + + if (browser != null) { + reqtrans.set("http_browser", browser); + } + + String authorization = request.getHeader("authorization"); + + if (authorization != null) { + reqtrans.set("authorization", authorization); + } + + // response.setHeader ("Server", "Helma/"+helma.main.Server.version); + reqtrans.path = getPathInfo(request); + + ResponseTrans restrans = execute(reqtrans); + + // set cookies + int ncookies = restrans.countCookies(); + + if (restrans.countCookies() > 0) { + CookieTrans[] resCookies = restrans.getCookies(); + + for (int i = 0; i < resCookies.length; i++) + try { + Cookie c = resCookies[i].getCookie("/", resCookieDomain); + + response.addCookie(c); + } catch (Exception ignore) { + } + } + + // write response + writeResponse(request, response, restrans); + } catch (Exception x) { + try { + if (debug) { + sendError(response, response.SC_INTERNAL_SERVER_ERROR, + "Error in request handler:" + x); + } else { + sendError(response, response.SC_INTERNAL_SERVER_ERROR, + "The server encountered an error while processing your request. " + + "Please check back later."); + } + + log("Exception in execute: " + x); + } catch (IOException io_e) { + log("Exception in sendError: " + io_e); + } + } } + void writeResponse(HttpServletRequest req, HttpServletResponse res, + ResponseTrans hopres) { + if (hopres.getETag() != null) { + res.setHeader("ETag", hopres.getETag()); + } - void sendError (HttpServletResponse response, int code, String message) - throws IOException { - response.reset (); - response.setStatus (code); - response.setContentType ("text/html"); - Writer writer = response.getWriter (); - writer.write (message); - writer.flush (); + if (hopres.getRedirect() != null) { + sendRedirect(req, res, hopres.getRedirect()); + } else if (hopres.getNotModified()) { + res.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + } else { + if (!hopres.cache || !caching) { + // Disable caching of response. + // for HTTP 1.0 + res.setDateHeader("Expires", System.currentTimeMillis() - 10000); + res.setHeader("Pragma", "no-cache"); + + // for HTTP 1.1 + res.setHeader("Cache-Control", + "no-cache, no-store, must-revalidate, max-age=0"); + } + + if (hopres.realm != null) { + res.setHeader("WWW-Authenticate", "Basic realm=\"" + hopres.realm + "\""); + } + + if (hopres.status > 0) { + res.setStatus(hopres.status); + } + + // set last-modified header to now + long modified = hopres.getLastModified(); + + if (modified > -1) { + res.setDateHeader("Last-Modified", System.currentTimeMillis()); + } + + // if we don't know which charset to use for parsing HTTP params, + // take the one from the response. This usually works because + // browsers send parrameters in the same encoding as the page + // containing the form has. Problem is we can do this only per servlet, + // not per session or even per page, which would produce too much overhead + if (defaultEncoding == null) { + defaultEncoding = hopres.charset; + } + + res.setContentLength(hopres.getContentLength()); + res.setContentType(hopres.getContentType()); + + try { + OutputStream out = res.getOutputStream(); + + out.write(hopres.getContent()); + out.flush(); + } catch (Exception io_e) { + log("Exception in writeResponse: " + io_e); + } + } } - void sendRedirect (HttpServletRequest req, HttpServletResponse res, String url) { - String location = url; - if (url.indexOf ("://") == -1) - { - // need to transform a relative URL into an absolute one - String scheme = req.getScheme (); - StringBuffer loc = new StringBuffer(scheme); - loc.append ("://"); - loc.append (req.getServerName ()); - int p = req.getServerPort (); - // check if we need to include server port - if (p > 0 && - (("http".equals(scheme) && p != 80) || - ("https".equals(scheme) && p != 443))) - { - loc.append (":"); - loc.append (p); - } - if (!url.startsWith ("/")) - { - String requri = req.getRequestURI (); - int lastSlash = requri.lastIndexOf ("/"); - if (lastSlash == requri.length()-1) - loc.append (requri); - else if (lastSlash > -1) - loc.append (requri.substring (0, lastSlash+1)); - else - loc.append ("/"); - } - loc.append (url); - location = loc.toString (); - } - // send status code 303 for HTTP 1.1, 302 otherwise - if (isOneDotOne (req.getProtocol ())) - res.setStatus (res.SC_SEE_OTHER); - else - res.setStatus (res.SC_MOVED_TEMPORARILY); - res.setContentType ("text/html"); - res.setHeader ("Location", location); + void sendError(HttpServletResponse response, int code, String message) + throws IOException { + response.reset(); + response.setStatus(code); + response.setContentType("text/html"); + + Writer writer = response.getWriter(); + + writer.write(message); + writer.flush(); } - FileUpload getUpload (HttpServletRequest request) throws Exception { - int contentLength = request.getContentLength (); - BufferedInputStream in = new BufferedInputStream (request.getInputStream ()); - if (contentLength > uploadLimit*1024) { - throw new RuntimeException ("Upload exceeds limit of "+uploadLimit+" kb."); - } - String contentType = request.getContentType (); - FileUpload upload = new FileUpload(uploadLimit); - upload.load (in, contentType, contentLength); - return upload; + void sendRedirect(HttpServletRequest req, HttpServletResponse res, String url) { + String location = url; + + if (url.indexOf("://") == -1) { + // need to transform a relative URL into an absolute one + String scheme = req.getScheme(); + StringBuffer loc = new StringBuffer(scheme); + + loc.append("://"); + loc.append(req.getServerName()); + + int p = req.getServerPort(); + + // check if we need to include server port + if ((p > 0) && + (("http".equals(scheme) && (p != 80)) || + ("https".equals(scheme) && (p != 443)))) { + loc.append(":"); + loc.append(p); + } + + if (!url.startsWith("/")) { + String requri = req.getRequestURI(); + int lastSlash = requri.lastIndexOf("/"); + + if (lastSlash == (requri.length() - 1)) { + loc.append(requri); + } else if (lastSlash > -1) { + loc.append(requri.substring(0, lastSlash + 1)); + } else { + loc.append("/"); + } + } + + loc.append(url); + location = loc.toString(); + } + + // send status code 303 for HTTP 1.1, 302 otherwise + if (isOneDotOne(req.getProtocol())) { + res.setStatus(res.SC_SEE_OTHER); + } else { + res.setStatus(res.SC_MOVED_TEMPORARILY); + } + + res.setContentType("text/html"); + res.setHeader("Location", location); } + FileUpload getUpload(HttpServletRequest request) throws Exception { + int contentLength = request.getContentLength(); + BufferedInputStream in = new BufferedInputStream(request.getInputStream()); + + if (contentLength > (uploadLimit * 1024)) { + throw new RuntimeException("Upload exceeds limit of " + uploadLimit + " kb."); + } + + String contentType = request.getContentType(); + FileUpload upload = new FileUpload(uploadLimit); + + upload.load(in, contentType, contentLength); + + return upload; + } Object getUploadPart(FileUpload upload, String name) { - return upload.getParts().get(name); + return upload.getParts().get(name); } - /** * Put name value pair in map. * @@ -352,9 +454,10 @@ public abstract class AbstractServletClient extends HttpServlet { * Put name and value pair in map. When name already exist, add value * to array of values. */ - private static void putMapEntry( Map map, String name, String value) { + private static void putMapEntry(Map map, String name, String value) { String[] newValues = null; String[] oldValues = (String[]) map.get(name); + if (oldValues == null) { newValues = new String[1]; newValues[0] = value; @@ -363,42 +466,49 @@ public abstract class AbstractServletClient extends HttpServlet { System.arraycopy(oldValues, 0, newValues, 0, oldValues.length); newValues[oldValues.length] = value; } + map.put(name, newValues); } + protected Map parseParameters(HttpServletRequest request) { + String encoding = request.getCharacterEncoding(); - - protected Map parseParameters (HttpServletRequest request) { - - String encoding = request.getCharacterEncoding (); - if (encoding == null) + if (encoding == null) { // no encoding from request, use standard one encoding = defaultEncoding; - if (encoding == null) - encoding = "ISO-8859-1"; + } + + if (encoding == null) { + encoding = "ISO-8859-1"; + } + + HashMap parameters = new HashMap(); - HashMap parameters = new HashMap (); // Parse any query string parameters from the request try { - parseParameters (parameters, request.getQueryString().getBytes(), encoding); + parseParameters(parameters, request.getQueryString().getBytes(), encoding); } catch (Exception e) { } // Parse any posted parameters in the input stream if ("POST".equals(request.getMethod()) && - "application/x-www-form-urlencoded".equals(request.getContentType())) { + "application/x-www-form-urlencoded".equals(request.getContentType())) { try { int max = request.getContentLength(); int len = 0; - byte buf[] = new byte[max]; + byte[] buf = new byte[max]; ServletInputStream is = request.getInputStream(); + while (len < max) { int next = is.read(buf, len, max - len); - if (next < 0 ) { + + if (next < 0) { break; } + len += next; } + // is.close(); parseParameters(parameters, buf, encoding); } catch (IllegalArgumentException e) { @@ -406,7 +516,7 @@ public abstract class AbstractServletClient extends HttpServlet { } } - return parameters; + return parameters; } /** @@ -428,41 +538,53 @@ public abstract class AbstractServletClient extends HttpServlet { * * @exception UnsupportedEncodingException if the data is malformed */ - public static void parseParameters (Map map, byte[] data, String encoding) - throws UnsupportedEncodingException { - - if (data != null && data.length > 0) { - int pos = 0; - int ix = 0; - int ox = 0; + public static void parseParameters(Map map, byte[] data, String encoding) + throws UnsupportedEncodingException { + if ((data != null) && (data.length > 0)) { + int pos = 0; + int ix = 0; + int ox = 0; String key = null; String value = null; + while (ix < data.length) { byte c = data[ix++]; + switch ((char) c) { - case '&': - value = new String(data, 0, ox, encoding); - if (key != null) { - putMapEntry(map, key, value); - key = null; - } - ox = 0; - break; - case '=': - key = new String(data, 0, ox, encoding); - ox = 0; - break; - case '+': - data[ox++] = (byte)' '; - break; - case '%': - data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4) - + convertHexDigit(data[ix++])); - break; - default: - data[ox++] = c; + case '&': + value = new String(data, 0, ox, encoding); + + if (key != null) { + putMapEntry(map, key, value); + key = null; + } + + ox = 0; + + break; + + case '=': + key = new String(data, 0, ox, encoding); + ox = 0; + + break; + + case '+': + data[ox++] = (byte) ' '; + + break; + + case '%': + data[ox++] = (byte) ((convertHexDigit(data[ix++]) << 4) + + convertHexDigit(data[ix++])); + + break; + + default: + data[ox++] = c; } } + //The last value does not end in '&'. So save it now. if (key != null) { value = new String(data, 0, ox, encoding); @@ -476,48 +598,73 @@ public abstract class AbstractServletClient extends HttpServlet { * * @param b the character value byte */ - private static byte convertHexDigit( byte b ) { - if ((b >= '0') && (b <= '9')) return (byte)(b - '0'); - if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10); - if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10); + private static byte convertHexDigit(byte b) { + if ((b >= '0') && (b <= '9')) { + return (byte) (b - '0'); + } + + if ((b >= 'a') && (b <= 'f')) { + return (byte) (b - 'a' + 10); + } + + if ((b >= 'A') && (b <= 'F')) { + return (byte) (b - 'A' + 10); + } + return 0; } - boolean isOneDotOne (String protocol) { - if (protocol == null) - return false; - if (protocol.endsWith ("1.1")) - return true; - return false; + boolean isOneDotOne(String protocol) { + if (protocol == null) { + return false; + } + + if (protocol.endsWith("1.1")) { + return true; + } + + return false; } - String getPathInfo (HttpServletRequest req) { - StringTokenizer t = new StringTokenizer (req.getContextPath(), "/"); - int prefixTokens = t.countTokens(); - t = new StringTokenizer (req.getServletPath(), "/"); - prefixTokens += t.countTokens(); - - t = new StringTokenizer (req.getRequestURI(), "/"); - int uriTokens = t.countTokens(); - StringBuffer pathbuffer = new StringBuffer (); - for (int i=0; i prefixTokens) - pathbuffer.append ("/"); - if (token.indexOf ("+") == -1 && token.indexOf ("%") == -1) - pathbuffer.append (token); - else - pathbuffer.append (URLDecoder.decode (token)); - } - return pathbuffer.toString (); + String getPathInfo(HttpServletRequest req) { + StringTokenizer t = new StringTokenizer(req.getContextPath(), "/"); + int prefixTokens = t.countTokens(); + + t = new StringTokenizer(req.getServletPath(), "/"); + prefixTokens += t.countTokens(); + + t = new StringTokenizer(req.getRequestURI(), "/"); + + int uriTokens = t.countTokens(); + StringBuffer pathbuffer = new StringBuffer(); + + for (int i = 0; i < uriTokens; i++) { + String token = t.nextToken(); + + if (i < prefixTokens) { + continue; + } + + if (i > prefixTokens) { + pathbuffer.append("/"); + } + + if ((token.indexOf("+") == -1) && (token.indexOf("%") == -1)) { + pathbuffer.append(token); + } else { + pathbuffer.append(URLDecoder.decode(token)); + } + } + + return pathbuffer.toString(); } - public String getServletInfo(){ - return new String("Helma Servlet Client"); + /** + * + * + * @return ... + */ + public String getServletInfo() { + return new String("Helma Servlet Client"); } - - } - diff --git a/src/helma/servlet/EmbeddedServletClient.java b/src/helma/servlet/EmbeddedServletClient.java index 6724278c..e2b621b1 100644 --- a/src/helma/servlet/EmbeddedServletClient.java +++ b/src/helma/servlet/EmbeddedServletClient.java @@ -1,51 +1,74 @@ -// EmbeddedServletClient.java -// Copyright (c) Hannes Wallnöfer, 2002 +/* + * 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.servlet; -import javax.servlet.*; -import java.io.*; -import java.util.*; import helma.framework.*; import helma.framework.core.Application; import helma.main.*; import helma.util.*; +import java.io.*; +import java.util.*; +import javax.servlet.*; /** * Servlet client that runs a Helma application for the embedded * web server */ - public final class EmbeddedServletClient extends AbstractServletClient { - private Application app = null; private String appName; // The path where this servlet is mounted String mountpoint; - public EmbeddedServletClient () { - super (); + /** + * Creates a new EmbeddedServletClient object. + */ + public EmbeddedServletClient() { + super(); } + /** + * + * + * @param init ... + * + * @throws ServletException ... + */ + public void init(ServletConfig init) throws ServletException { + super.init(init); + appName = init.getInitParameter("application"); - public void init (ServletConfig init) throws ServletException { - super.init (init); - appName = init.getInitParameter ("application"); - if (appName == null) - throw new ServletException ("Application name not set in init parameters"); - mountpoint = init.getInitParameter ("mountpoint"); - if (mountpoint == null) - mountpoint = "/"+appName; + if (appName == null) { + 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 { - if (app == null) - app = Server.getServer().getApplication (appName); - return app.execute (req); - } + ResponseTrans execute(RequestTrans req) throws Exception { + if (app == null) { + app = Server.getServer().getApplication(appName); + } + return app.execute(req); + } } - - - diff --git a/src/helma/servlet/MultiServletClient.java b/src/helma/servlet/MultiServletClient.java index 4100f48c..29a98f0a 100644 --- a/src/helma/servlet/MultiServletClient.java +++ b/src/helma/servlet/MultiServletClient.java @@ -1,82 +1,111 @@ -// MultiServletClient.java -// Copyright (c) Hannes Wallnöfer 2001 - +/* + * 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.servlet; -import javax.servlet.*; -import javax.servlet.http.*; +import helma.framework.*; import java.io.*; import java.rmi.Naming; import java.rmi.RemoteException; import java.util.Hashtable; -import helma.framework.*; +import javax.servlet.*; +import javax.servlet.http.*; /** * This is the HOP servlet adapter. This class communicates with any * Hop application on a given Hop server, extracting the application name * from the request path. */ - public class MultiServletClient extends AbstractServletClient { - private Hashtable apps; - public void init (ServletConfig init) throws ServletException { - super.init (init); - apps = new Hashtable (); - host = init.getInitParameter ("host"); - if (host == null) - host = "localhost"; - String portstr = init.getInitParameter ("port"); - port = portstr == null ? 5055 : Integer.parseInt (portstr); - hopUrl = "//" + host + ":" + port + "/"; + /** + * + * + * @param init ... + * + * @throws ServletException ... + */ + public void init(ServletConfig init) throws ServletException { + super.init(init); + apps = new Hashtable(); + host = init.getInitParameter("host"); + + if (host == null) { + host = "localhost"; + } + + String portstr = init.getInitParameter("port"); + + port = (portstr == null) ? 5055 : Integer.parseInt(portstr); + hopUrl = "//" + host + ":" + port + "/"; } - public void destroy () { - if (apps != null) { - apps.clear (); - apps = null; - } + /** + * + */ + public void destroy() { + if (apps != null) { + apps.clear(); + apps = null; + } } - ResponseTrans execute (RequestTrans req) throws Exception { - // the app-id is the first element in the request path - // so we have to first get than and than rewrite req.path. - int slash = req.path.indexOf ("/"); - String appId = null; - if (slash == -1) { - // no slash found, path equals app-id - appId = req.path; - req.path = ""; - } else { - // cut path into app id and rewritten path - appId = req.path.substring (0, slash); - req.path = req.path.substring (slash+1); - } - IRemoteApp app = getApp (appId); - try { - return app.execute (req); - } catch (Exception x) { - invalidateApp (appId); - app = getApp (appId); - return app.execute (req); - } + ResponseTrans execute(RequestTrans req) throws Exception { + // the app-id is the first element in the request path + // so we have to first get than and than rewrite req.path. + int slash = req.path.indexOf("/"); + String appId = null; + + if (slash == -1) { + // no slash found, path equals app-id + appId = req.path; + req.path = ""; + } else { + // cut path into app id and rewritten path + appId = req.path.substring(0, slash); + req.path = req.path.substring(slash + 1); + } + + IRemoteApp app = getApp(appId); + + try { + return app.execute(req); + } catch (Exception x) { + invalidateApp(appId); + app = getApp(appId); + + return app.execute(req); + } } - IRemoteApp getApp (String appId) throws Exception { - IRemoteApp app = (IRemoteApp) apps.get (appId); - if (app != null) - return app; - app = (IRemoteApp) Naming.lookup (hopUrl + appId); - apps.put (appId, app); - return app; + IRemoteApp getApp(String appId) throws Exception { + IRemoteApp app = (IRemoteApp) apps.get(appId); + + if (app != null) { + return app; + } + + app = (IRemoteApp) Naming.lookup(hopUrl + appId); + apps.put(appId, app); + + return app; } - void invalidateApp (String appId) { - apps.remove (appId); + void invalidateApp(String appId) { + apps.remove(appId); } - - } - diff --git a/src/helma/servlet/ServletClient.java b/src/helma/servlet/ServletClient.java index e8751f72..45bab3fd 100644 --- a/src/helma/servlet/ServletClient.java +++ b/src/helma/servlet/ServletClient.java @@ -1,62 +1,86 @@ -// ServletClient.java -// Copyright (c) Hannes Wallnöfer, Raphael Spannocchi 1998-2002 - +/* + * 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.servlet; -import javax.servlet.*; +import helma.framework.*; import java.io.*; import java.rmi.Naming; import java.rmi.RemoteException; import java.util.*; -import helma.framework.*; +import javax.servlet.*; /** * This is the standard Helma servlet adapter. This class represents a servlet * that is dedicated to one Helma application over RMI. */ - public class ServletClient extends AbstractServletClient { - private IRemoteApp app = null; private String appName = null; - public void init (ServletConfig init) throws ServletException { - super.init (init); - appName = init.getInitParameter ("application"); - host = init.getInitParameter ("host"); - if (host == null) - host = "localhost"; - String portstr = init.getInitParameter ("port"); - port = portstr == null ? 5055 : Integer.parseInt (portstr); - hopUrl = "//" + host + ":" + port + "/"; - if (appName == null) - throw new ServletException ("Application name not specified for helma.servlet.ServletClient"); + /** + * + * + * @param init ... + * + * @throws ServletException ... + */ + public void init(ServletConfig init) throws ServletException { + super.init(init); + appName = init.getInitParameter("application"); + host = init.getInitParameter("host"); + + if (host == null) { + host = "localhost"; + } + + String portstr = init.getInitParameter("port"); + + port = (portstr == null) ? 5055 : Integer.parseInt(portstr); + hopUrl = "//" + host + ":" + port + "/"; + + if (appName == null) { + throw new ServletException("Application name not specified for helma.servlet.ServletClient"); + } } - public void destroy () { - if (app != null) { - app = null; - } + /** + * + */ + public void destroy() { + if (app != null) { + app = null; + } } + ResponseTrans execute(RequestTrans req) throws Exception { + if (app == null) { + initApp(); + } - ResponseTrans execute (RequestTrans req) throws Exception { - if (app == null) - initApp (); - try { - return app.execute (req); - } catch (Exception x) { - initApp (); - return app.execute (req); - } + try { + return app.execute(req); + } catch (Exception x) { + initApp(); + + return app.execute(req); + } } - synchronized void initApp () throws Exception { - app = (IRemoteApp) Naming.lookup (hopUrl + appName); + synchronized void initApp() throws Exception { + app = (IRemoteApp) Naming.lookup(hopUrl + appName); } - - } - - diff --git a/src/helma/servlet/StandaloneServletClient.java b/src/helma/servlet/StandaloneServletClient.java index 4ae5a8ca..53b1c869 100644 --- a/src/helma/servlet/StandaloneServletClient.java +++ b/src/helma/servlet/StandaloneServletClient.java @@ -1,14 +1,27 @@ -// StandaloneServletClient.java -// Copyright (c) Hannes Wallnöfer, 2001 +/* + * 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.servlet; -import javax.servlet.*; -import java.io.*; -import java.util.*; import helma.framework.*; import helma.framework.core.Application; import helma.util.*; +import java.io.*; +import java.util.*; +import javax.servlet.*; /** * Standalone servlet client that runs a Helma application all by itself @@ -22,75 +35,85 @@ import helma.util.*; *
  • dbdir - the path of the embedded XML data store
  • * */ - public final class StandaloneServletClient extends AbstractServletClient { - private Application app = null; private String appName; private String appDir; private String dbDir; + /** + * + * + * @param init ... + * + * @throws ServletException ... + */ + public void init(ServletConfig init) throws ServletException { + super.init(init); - public void init (ServletConfig init) throws ServletException { - super.init (init); - - appName = init.getInitParameter ("application"); - if (appName == null || appName.trim().length() == 0) - throw new ServletException ("application parameter not specified"); + appName = init.getInitParameter("application"); - appDir = init.getInitParameter ("appdir"); - if (appDir == null || appDir.trim().length() == 0) - throw new ServletException ("appdir parameter not specified"); + if ((appName == null) || (appName.trim().length() == 0)) { + throw new ServletException("application parameter not specified"); + } - dbDir = init.getInitParameter ("dbdir"); - if (dbDir == null || dbDir.trim().length() == 0) - throw new ServletException ("dbdir parameter not specified"); -} + appDir = init.getInitParameter("appdir"); - ResponseTrans execute (RequestTrans req) throws Exception { - if (app == null) - createApp (); - return app.execute (req); + if ((appDir == null) || (appDir.trim().length() == 0)) { + throw new ServletException("appdir parameter not specified"); + } + + dbDir = init.getInitParameter("dbdir"); + + if ((dbDir == null) || (dbDir.trim().length() == 0)) { + throw new ServletException("dbdir parameter not specified"); + } } + ResponseTrans execute(RequestTrans req) throws Exception { + if (app == null) { + createApp(); + } + + return app.execute(req); + } /** * Create the application. Since we are synchronized only here, we * do another check if the app already exists and immediately return if it does. */ - synchronized void createApp () { - if (app != null) - return; - try { - File appHome = new File (appDir); - File dbHome = new File (dbDir); - app = new Application (appName, appHome, dbHome); - app.init (); - app.start (); - } catch (Exception x) { - log ("Error starting Application "+appName+": "+x); - x.printStackTrace (); - } - } + synchronized void createApp() { + if (app != null) { + return; + } + try { + File appHome = new File(appDir); + File dbHome = new File(dbDir); + + app = new Application(appName, appHome, dbHome); + app.init(); + app.start(); + } catch (Exception x) { + log("Error starting Application " + appName + ": " + x); + x.printStackTrace(); + } + } /** * The servlet is being destroyed. Close and release the application if * it does exist. */ - public void destroy () { - if (app != null) { - try { - app.stop (); - } catch (Exception x) { - log ("Error shutting down app "+app.getName()+": "); - x.printStackTrace (); - } - } - app = null; + public void destroy() { + if (app != null) { + try { + app.stop(); + } catch (Exception x) { + log("Error shutting down app " + app.getName() + ": "); + x.printStackTrace(); + } + } + + app = null; } - } - - - diff --git a/src/helma/util/CronJob.java b/src/helma/util/CronJob.java index 05ce44ac..159fe2af 100644 --- a/src/helma/util/CronJob.java +++ b/src/helma/util/CronJob.java @@ -86,7 +86,7 @@ public class CronJob { private String name = null; private String function = null; - private long timeout = 30000; + private long timeout = 20000; /** A method for parsing properties. It looks through the properties * file for entries that look like this: @@ -161,7 +161,26 @@ public class CronJob { */ - public static Collection parse(Properties props) { + public static CronJob newJob (String functionName, String year, String month, String day, String weekday, String hour, String minute) { + CronJob job = new CronJob (functionName); + job.setFunction (functionName); + if (year != null) + parseYear (job, year); + if (month != null) + parseMonth (job, month); + if (day != null) + parseDay (job, day); + if (weekday != null) + parseWeekDay (job, weekday); + if (hour != null) + parseHour (job, hour); + if (minute != null) + parseMinute (job, minute); + return job; + } + + + public static Vector parse(Properties props) { Hashtable jobs = new Hashtable (); Enumeration e = props.keys (); while (e.hasMoreElements ()) { @@ -195,13 +214,40 @@ public class CronJob { parseHour (job, value); } else if (jobSpec.equalsIgnoreCase("minute")) { parseMinute (job, value); + } else if (jobSpec.equalsIgnoreCase("timeout")) { + parseTimeout (job, value); } } catch (NoSuchElementException nsee) { } } - return jobs.values (); + Vector jobVec = new Vector (jobs.values ()); + return (Vector) sort (jobVec); } + public static List sort (List list) { + Collections.sort (list, new Comparator() { + public int compare (Object o1, Object o2) { + CronJob cron1 = (CronJob) o1; + CronJob cron2 = (CronJob) o2; + if (cron1.getTimeout () > cron2.getTimeout ()) + return 1; + else if (cron1.getTimeout () < cron2.getTimeout ()) + return -1; + else + return 0; + } + public boolean equals (Object o1, Object o2) { + if (o1!=null) { + return o1.equals (o2); + } else { + return false; + } + } + + }); + return list; + } + public static void parseYear (CronJob job, String value) { if (value.equals("*")) { @@ -351,6 +397,22 @@ public class CronJob { } + public static void parseTimeout (CronJob job, String timeout) { + long timeoutValue = 1000 * Long.valueOf(timeout).longValue (); + job.setTimeout (timeoutValue); + } + + public static long nextFullMinute () { + long now = System.currentTimeMillis(); + long millisAfterMinute = (now % 60000); + return (now + 60000 - millisAfterMinute); + } + + public static long millisToNextFullMinute () { + long now = System.currentTimeMillis(); + long millisAfterMinute = (now % 60000); + return (60000 - millisAfterMinute); + } /** * Create an empty CronJob. @@ -657,13 +719,6 @@ public class CronJob { this.timeout = timeout; } - /** - * Set this entry's timeout - */ - public void setTimeout(String timeout) - { - this.timeout = Long.valueOf(timeout).longValue (); - } /** * Get this entry's timeout @@ -672,4 +727,9 @@ public class CronJob { { return this.timeout; } + + public String toString () { + return "[CronJob " + name + "]"; + } + } diff --git a/src/helma/util/CryptFile.java b/src/helma/util/CryptFile.java index e33228c0..bc44c679 100644 --- a/src/helma/util/CryptFile.java +++ b/src/helma/util/CryptFile.java @@ -1,78 +1,128 @@ -// CryptFile.java -// Copyright (c) Hannes Wallnöfer 2001 +/* + * 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.*; import java.io.*; +import java.util.*; /** * This file authenticates against a passwd file */ - public class CryptFile { - private Properties users; private CryptFile parentFile; private File file; private long lastRead = 0; - public CryptFile (File file, CryptFile parentFile) { - this.file = file; - this.parentFile = parentFile; - users = new Properties (); + /** + * Creates a new CryptFile object. + * + * @param file ... + * @param parentFile ... + */ + public CryptFile(File file, CryptFile parentFile) { + this.file = file; + this.parentFile = parentFile; + users = new Properties(); } + /** + * + * + * @param username ... + * @param pw ... + * + * @return ... + */ + public boolean authenticate(String username, String pw) { + if (file.exists() && (file.lastModified() > lastRead)) { + readFile(); + } else if (!file.exists() && (users.size() > 0)) { + users.clear(); + } - public boolean authenticate (String username, String pw) { - if (file.exists () && file.lastModified () > lastRead) - readFile (); - else if (!file.exists () && users.size () > 0) - users.clear (); - String realpw = users.getProperty (username); - if (realpw != null) { - try { - // check if password matches - // first we try with unix crypt algorithm - String cryptpw = Crypt.crypt (realpw, pw); - if (realpw.equals (cryptpw)) - return true; - // then try MD5 - if (realpw.equals (MD5Encoder.encode (pw))) - return true; - } catch (Exception x) { - return false; - } - } else { - if (parentFile != null) - return parentFile.authenticate (username, pw); - } - return false; + String realpw = users.getProperty(username); + + if (realpw != null) { + try { + // check if password matches + // first we try with unix crypt algorithm + String cryptpw = Crypt.crypt(realpw, pw); + + if (realpw.equals(cryptpw)) { + return true; + } + + // then try MD5 + if (realpw.equals(MD5Encoder.encode(pw))) { + return true; + } + } catch (Exception x) { + return false; + } + } else { + if (parentFile != null) { + return parentFile.authenticate(username, pw); + } + } + + return false; } - private synchronized void readFile () { - BufferedReader reader = null; - users = new Properties (); - try { - reader = new BufferedReader (new FileReader (file)); - String line = reader.readLine (); - while (line != null) { - StringTokenizer st = new StringTokenizer (line, ":"); - if (st.countTokens () > 1) - users.put (st.nextToken (), st.nextToken ()); - line = reader.readLine (); - } - } catch (Exception ignore) { - } finally { - if (reader != null) - try { reader.close (); } catch (Exception x) {} - lastRead = System.currentTimeMillis (); - } + private synchronized void readFile() { + BufferedReader reader = null; + + users = new Properties(); + + try { + reader = new BufferedReader(new FileReader(file)); + + String line = reader.readLine(); + + while (line != null) { + StringTokenizer st = new StringTokenizer(line, ":"); + + if (st.countTokens() > 1) { + users.put(st.nextToken(), st.nextToken()); + } + + line = reader.readLine(); + } + } catch (Exception ignore) { + } finally { + if (reader != null) { + try { + reader.close(); + } catch (Exception x) { + } + } + + lastRead = System.currentTimeMillis(); + } } - public static void main (String args[]) { - CryptFile cf = new CryptFile (new File ("passwd"), null); - System.err.println (cf.authenticate ("hns", "asdf")); - } + /** + * + * + * @param args ... + */ + public static void main(String[] args) { + CryptFile cf = new CryptFile(new File("passwd"), null); + System.err.println(cf.authenticate("hns", "asdf")); + } } diff --git a/src/helma/util/EmptyEnumeration.java b/src/helma/util/EmptyEnumeration.java index a5157fea..36765c1e 100644 --- a/src/helma/util/EmptyEnumeration.java +++ b/src/helma/util/EmptyEnumeration.java @@ -1,5 +1,18 @@ -// EmptyEnumeration.java -// Copyright (c) Hannes Wallnöfer 2001 +/* + * 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; @@ -8,16 +21,22 @@ import java.util.Enumeration; /** * Utility class for empty enum */ - public class EmptyEnumeration implements Enumeration { - - - public boolean hasMoreElements () { - return false; + /** + * + * + * @return ... + */ + public boolean hasMoreElements() { + return false; } - public Object nextElement () { - return null; + /** + * + * + * @return ... + */ + public Object nextElement() { + return null; } - } diff --git a/src/helma/util/FileUpload.java b/src/helma/util/FileUpload.java index d22e5aad..1b8b80a3 100644 --- a/src/helma/util/FileUpload.java +++ b/src/helma/util/FileUpload.java @@ -1,5 +1,18 @@ -// FileUpload.java -// Copyright (c) Hannes Wallnöfer 1996-2000 +/* + * 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; @@ -10,99 +23,138 @@ import java.util.*; /** * Utility class for MIME file uploads via HTTP POST. */ - public class FileUpload { - public Hashtable parts; int maxKbytes; - public FileUpload () { - maxKbytes = 1024; + /** + * Creates a new FileUpload object. + */ + public FileUpload() { + maxKbytes = 1024; } - public FileUpload (int max) { - maxKbytes = max; + /** + * Creates a new FileUpload object. + * + * @param max ... + */ + public FileUpload(int max) { + maxKbytes = max; } - public Hashtable getParts () { - return parts; + /** + * + * + * @return ... + */ + public Hashtable getParts() { + return parts; } + /** + * + * + * @param is ... + * @param contentType ... + * @param contentLength ... + * + * @throws Exception ... + * @throws MimeParserException ... + * @throws IOException ... + */ + public void load(InputStream is, String contentType, int contentLength) + throws Exception { + parts = new Hashtable(); - public void load (InputStream is, String contentType, int contentLength) throws Exception { + String boundary = getSubHeader(contentType, "boundary"); - parts = new Hashtable (); + if (boundary == null) { + throw new MimeParserException("Error parsing MIME input stream."); + } - String boundary = getSubHeader (contentType, "boundary"); - if (boundary == null) - throw new MimeParserException ("Error parsing MIME input stream."); - if (maxKbytes > -1 && contentLength > maxKbytes*1024) - throw new IOException ("Size of upload exceeds limit of " + maxKbytes + " kB."); + if ((maxKbytes > -1) && (contentLength > (maxKbytes * 1024))) { + throw new IOException("Size of upload exceeds limit of " + maxKbytes + + " kB."); + } - byte b[] = new byte[contentLength]; - MultipartInputStream in = new MultipartInputStream (new BufferedInputStream (is), boundary.getBytes ()); + byte[] b = new byte[contentLength]; + MultipartInputStream in = new MultipartInputStream(new BufferedInputStream(is), + boundary.getBytes()); - while (in.nextInputStream ()) { + while (in.nextInputStream()) { + MimeParser parser = new MimeParser(in, new MimeHeadersFactory()); + MimeHeaders headers = (MimeHeaders) parser.parse(); - MimeParser parser = new MimeParser (in, new MimeHeadersFactory ()); - MimeHeaders headers = (MimeHeaders) parser.parse (); + InputStream bodystream = parser.getInputStream(); + int read; + int count = 0; - InputStream bodystream = parser.getInputStream (); - int read, count = 0; - - while ((read = bodystream.read (b, count, 4096)) > -1) { - count += read; - if (count == b.length) { - byte newb[] = new byte[count+4096]; - System.arraycopy (b, 0, newb, 0, count); - b = newb; - } - } + while ((read = bodystream.read(b, count, 4096)) > -1) { + count += read; - byte newb[] = new byte[count]; - System.arraycopy (b, 0, newb, 0, count); + if (count == b.length) { + byte[] newb = new byte[count + 4096]; - String type = headers.getValue("Content-Type"); - String disposition = headers.getValue ("Content-Disposition"); - String name = getSubHeader (disposition, "name"); - String filename = getSubHeader (disposition, "filename"); - if (filename != null) { - int sep = filename.lastIndexOf ("\\"); - if (sep > -1) - filename = filename.substring (sep+1); - sep = filename.lastIndexOf ("/"); - if (sep > -1) - filename = filename.substring (sep+1); - } - if (filename != null) { - MimePart part = new MimePart (filename, newb, type); - parts.put (name, part); - } else { - parts.put (name, new String (newb)); - } + System.arraycopy(b, 0, newb, 0, count); + b = newb; + } + } - } + byte[] newb = new byte[count]; + System.arraycopy(b, 0, newb, 0, count); + + String type = headers.getValue("Content-Type"); + String disposition = headers.getValue("Content-Disposition"); + String name = getSubHeader(disposition, "name"); + String filename = getSubHeader(disposition, "filename"); + + if (filename != null) { + int sep = filename.lastIndexOf("\\"); + + if (sep > -1) { + filename = filename.substring(sep + 1); + } + + sep = filename.lastIndexOf("/"); + + if (sep > -1) { + filename = filename.substring(sep + 1); + } + } + + if (filename != null) { + MimePart part = new MimePart(filename, newb, type); + + parts.put(name, part); + } else { + parts.put(name, new String(newb)); + } + } } + private String getSubHeader(String header, String subHeaderName) { + if (header == null) { + return null; + } + String retval = null; + StringTokenizer headerTokenizer = new StringTokenizer(header, ";"); - private String getSubHeader (String header, String subHeaderName) { - if (header == null) - return null; - String retval = null; - StringTokenizer headerTokenizer = new StringTokenizer(header, ";"); - while (headerTokenizer.hasMoreTokens()) { - String token = headerTokenizer.nextToken().trim (); - int i = token.indexOf ("="); - if (i > 0) { - String hname = token.substring (0, i).trim (); - if (hname.equalsIgnoreCase (subHeaderName)) - retval = token.substring (i+1).replace ('"', ' ').trim (); - } - } - return retval; + while (headerTokenizer.hasMoreTokens()) { + String token = headerTokenizer.nextToken().trim(); + int i = token.indexOf("="); + + if (i > 0) { + String hname = token.substring(0, i).trim(); + + if (hname.equalsIgnoreCase(subHeaderName)) { + retval = token.substring(i + 1).replace('"', ' ').trim(); + } + } + } + + return retval; } - - } diff --git a/src/helma/util/HtmlEncoder.java b/src/helma/util/HtmlEncoder.java index 29c587ad..6bfbdf8b 100644 --- a/src/helma/util/HtmlEncoder.java +++ b/src/helma/util/HtmlEncoder.java @@ -1,23 +1,34 @@ -// HtmlEncoder.java -// Copyright (c) Hannes Wallnöfer 1997-2000 +/* + * 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.io.*; -import java.util.*; import java.awt.*; import java.awt.image.*; +import java.io.*; import java.text.*; - +import java.util.*; /** - * This is a utility class to encode special characters and do formatting + * This is a utility class to encode special characters and do formatting * for HTML output. */ - public final class HtmlEncoder { - // transformation table for characters 128 to 255. These actually fall into two + // transformation table for characters 128 to 255. These actually fall into two // groups, put together for efficiency: "Windows" chacacters 128-159 such as // "smart quotes", which are encoded to valid Unicode entities, and // valid ISO-8859 caracters 160-255, which are encoded to the symbolic HTML @@ -157,140 +168,158 @@ public final class HtmlEncoder { "ÿ" // 255 }; + static final HashSet allTags = new HashSet(); - static final HashSet allTags = new HashSet (); static { - allTags.add ("a"); - allTags.add ("abbr"); - allTags.add ("address"); - allTags.add ("applet"); - allTags.add ("area"); - allTags.add ("b"); - allTags.add ("base"); - allTags.add ("basefont"); - allTags.add ("bgsound"); - allTags.add ("big"); - allTags.add ("blink"); - allTags.add ("blockquote"); - allTags.add ("bq"); - allTags.add ("body"); - allTags.add ("br"); - allTags.add ("button"); - allTags.add ("caption"); - allTags.add ("center"); - allTags.add ("cite"); - allTags.add ("code"); - allTags.add ("col"); - allTags.add ("colgroup"); - allTags.add ("del"); - allTags.add ("dir"); - allTags.add ("div"); - allTags.add ("dl"); - allTags.add ("dt"); - allTags.add ("dd"); - allTags.add ("em"); - allTags.add ("embed"); - allTags.add ("fieldset"); - allTags.add ("font"); - allTags.add ("form"); - allTags.add ("frame"); - allTags.add ("frameset"); - allTags.add ("h1"); - allTags.add ("h2"); - allTags.add ("h3"); - allTags.add ("h4"); - allTags.add ("h5"); - allTags.add ("h6"); - allTags.add ("head"); - allTags.add ("html"); - allTags.add ("hr"); - allTags.add ("i"); - allTags.add ("iframe"); - allTags.add ("img"); - allTags.add ("input"); - allTags.add ("ins"); - allTags.add ("isindex"); - allTags.add ("kbd"); - allTags.add ("li"); - allTags.add ("link"); - allTags.add ("listing"); - allTags.add ("map"); - allTags.add ("marquee"); - allTags.add ("menu"); - allTags.add ("meta"); - allTags.add ("nobr"); - allTags.add ("noframes"); - allTags.add ("object"); - allTags.add ("ol"); - allTags.add ("option"); - allTags.add ("optgroup"); - allTags.add ("p"); - allTags.add ("param"); - allTags.add ("plaintext"); - allTags.add ("pre"); - allTags.add ("q"); - allTags.add ("s"); - allTags.add ("samp"); - allTags.add ("script"); - allTags.add ("select"); - allTags.add ("small"); - allTags.add ("span"); - allTags.add ("strike"); - allTags.add ("strong"); - allTags.add ("style"); - allTags.add ("sub"); - allTags.add ("sup"); - allTags.add ("table"); - allTags.add ("tbody"); - allTags.add ("td"); - allTags.add ("textarea"); - allTags.add ("tfoot"); - allTags.add ("th"); - allTags.add ("thead"); - allTags.add ("title"); - allTags.add ("tr"); - allTags.add ("tt"); - allTags.add ("u"); - allTags.add ("ul"); - allTags.add ("var"); - allTags.add ("wbr"); - allTags.add ("xmp"); + allTags.add("a"); + allTags.add("abbr"); + allTags.add("acronym"); + allTags.add("address"); + allTags.add("applet"); + allTags.add("area"); + allTags.add("b"); + allTags.add("base"); + allTags.add("basefont"); + allTags.add("bdo"); + allTags.add("bgsound"); + allTags.add("big"); + allTags.add("blink"); + allTags.add("blockquote"); + allTags.add("bq"); + allTags.add("body"); + allTags.add("br"); + allTags.add("button"); + allTags.add("caption"); + allTags.add("center"); + allTags.add("cite"); + allTags.add("code"); + allTags.add("col"); + allTags.add("colgroup"); + allTags.add("del"); + allTags.add("dfn"); + allTags.add("dir"); + allTags.add("div"); + allTags.add("dl"); + allTags.add("dt"); + allTags.add("dd"); + allTags.add("em"); + allTags.add("embed"); + allTags.add("fieldset"); + allTags.add("font"); + allTags.add("form"); + allTags.add("frame"); + allTags.add("frameset"); + allTags.add("h1"); + allTags.add("h2"); + allTags.add("h3"); + allTags.add("h4"); + allTags.add("h5"); + allTags.add("h6"); + allTags.add("head"); + allTags.add("html"); + allTags.add("hr"); + allTags.add("i"); + allTags.add("iframe"); + allTags.add("img"); + allTags.add("input"); + allTags.add("ins"); + allTags.add("isindex"); + allTags.add("kbd"); + allTags.add("label"); + allTags.add("legend"); + allTags.add("li"); + allTags.add("link"); + allTags.add("listing"); + allTags.add("map"); + allTags.add("marquee"); + allTags.add("menu"); + allTags.add("meta"); + allTags.add("nobr"); + allTags.add("noframes"); + allTags.add("noscript"); + allTags.add("object"); + allTags.add("ol"); + allTags.add("option"); + allTags.add("optgroup"); + allTags.add("p"); + allTags.add("param"); + allTags.add("plaintext"); + allTags.add("pre"); + allTags.add("q"); + allTags.add("s"); + allTags.add("samp"); + allTags.add("script"); + allTags.add("select"); + allTags.add("small"); + allTags.add("span"); + allTags.add("strike"); + allTags.add("strong"); + allTags.add("style"); + allTags.add("sub"); + allTags.add("sup"); + allTags.add("table"); + allTags.add("tbody"); + allTags.add("td"); + allTags.add("textarea"); + allTags.add("tfoot"); + allTags.add("th"); + allTags.add("thead"); + allTags.add("title"); + allTags.add("tr"); + allTags.add("tt"); + allTags.add("u"); + allTags.add("ul"); + allTags.add("var"); + allTags.add("wbr"); + allTags.add("xmp"); } - // tags which signal us to start suppressing \n ->
    encoding - // these are "structrural" tags, for example, we don't want to add
    s - // between a and a . - static final HashSet suppressLinebreakTags = new HashSet (); - static { - suppressLinebreakTags.add ("table"); - suppressLinebreakTags.add ("ul"); - suppressLinebreakTags.add ("ol"); - suppressLinebreakTags.add ("pre"); - } + // HTML block tags need to suppress automatic newline to
    + // conversion around them to look good. However, they differ + // in how many newlines around them should ignored. These sets + // help to treat each tag right in newline conversion. + static final HashSet swallowAll = new HashSet(); + static final HashSet swallowTwo = new HashSet(); + static final HashSet swallowOne = new HashSet(); - // tags which signal us to stop suppressing \n ->
    encoding - // these usually signal transition from structural tags to normal - // HTML text, e.g.
    - static final HashSet encodeLinebreakTags = new HashSet (); static { - encodeLinebreakTags.add ("td"); - encodeLinebreakTags.add ("th"); - encodeLinebreakTags.add ("li"); - } + // actual block level elements + swallowOne.add("address"); + swallowTwo.add("blockquote"); + swallowTwo.add("center"); + swallowOne.add("dir"); + swallowOne.add("div"); + swallowTwo.add("dl"); + swallowTwo.add("fieldset"); + swallowTwo.add("form"); + swallowTwo.add("h1"); + swallowTwo.add("h2"); + swallowTwo.add("h3"); + swallowTwo.add("h4"); + swallowTwo.add("h5"); + swallowTwo.add("h6"); + swallowTwo.add("hr"); + swallowTwo.add("isindex"); + swallowAll.add("menu"); + swallowAll.add("noframes"); + swallowAll.add("noscript"); + swallowTwo.add("ol"); + swallowTwo.add("p"); + swallowTwo.add("pre"); + swallowOne.add("table"); + swallowTwo.add("ul"); - /** - * - */ - public final static String encode (String str) { - if (str == null) - return null; - int l = str.length(); - if (l == 0) - return ""; - // try to make stringbuffer large enough from the start - StringBuffer ret = new StringBuffer (Math.round (l*1.4f)); - encode (str, ret); - return ret.toString(); + /// to be treated as block level elements + swallowTwo.add("dd"); + swallowTwo.add("dt"); + swallowTwo.add("frameset"); + swallowTwo.add("li"); + swallowAll.add("tbody"); + swallowTwo.add("td"); + swallowAll.add("tfoot"); + swallowOne.add("th"); + swallowAll.add("thead"); + swallowAll.add("tr"); } /** @@ -298,354 +327,580 @@ public final class HtmlEncoder { * Helma macros and HTML comments are passed through unescaped, while * other occurrences of '<', '>' and '&' are encoded to HTML entities. */ - public final static void encode (String str, StringBuffer ret) { - if (str == null) - return; + public final static String encode(String str) { + if (str == null) { + return null; + } - int l = str.length(); + int l = str.length(); - // are we currently within a < and a > that consitute some kind of tag? - // we use tag balancing to know whether we are inside a tag (and should - // pass things through unchanged) or outside (and should encode stuff). - boolean insideTag = false; - // are we inside an HTML tag? - boolean insideHtmlTag = false; - // if we are inside a tag, we encode everything to make - // documentation work easier - boolean insideCodeTag = false; - // are we within a Helma <% macro %> tag? We treat macro tags and - // comments specially, since we can't rely on tag balancing - // to know when we leave a macro tag or comment. - boolean insideMacroTag = false; - // are we inside an HTML comment? - boolean insideComment = false; - // the quotation mark we are in within an HTML or Macro tag, if any - char htmlQuoteChar = '\u0000'; - char macroQuoteChar = '\u0000'; - // the difference between swallowOneNewline and ignoreNewline is that - // swallowOneNewline is just effective once (for the next newline) - boolean ignoreNewline = false; - boolean swallowOneNewline = false; - // did we meet a backslash escape? - boolean escape = false; + if (l == 0) { + return ""; + } - for (int i=0; i tagStart && j < l) { - String tagName = str.substring (tagStart, j).toLowerCase(); - if ("code".equals (tagName) && insideCloseTag && insideCodeTag) - insideCodeTag = false; - if (allTags.contains (tagName) && !insideCodeTag) { - insideHtmlTag = insideTag = true; - htmlQuoteChar = '\u0000'; - // set ignoreNewline on some tags, depending on wheather they're - // being opened or closed. - // what's going on here? we switch newline encoding on inside some tags, for - // others we switch it on when they're closed - if (encodeLinebreakTags.contains (tagName)) { - ignoreNewline = insideCloseTag; - swallowOneNewline = true; - } else if (suppressLinebreakTags.contains (tagName)) { - ignoreNewline = !insideCloseTag; - swallowOneNewline = true; - } else if ("p".equals (tagName) || "blockquote".equals (tagName) || "bq".equals (tagName)) { - swallowOneNewline = true; - } - if ("code".equals (tagName) && !insideCloseTag) - insideCodeTag = true; - } - } - } - } // if (i < l-2) - if (insideTag) - ret.append ('<'); - else - ret.append ("<"); - break; - case '\\': - ret.append (c); - if (insideTag && !insideComment) - escape = !escape; - break; - case '"': - case '\'': - ret.append (c); - if (!insideComment) { - // check if the quote is escaped - if (insideMacroTag) { - if (escape) - escape = false; - else if (macroQuoteChar == c) - macroQuoteChar = '\u0000'; - else if (macroQuoteChar == '\u0000') - macroQuoteChar = c; - } else if (insideHtmlTag) { - if (escape) - escape = false; - else if (htmlQuoteChar == c) - htmlQuoteChar = '\u0000'; - else if (htmlQuoteChar == '\u0000') - htmlQuoteChar = c; - } - } - break; - case '\n': - ret.append ('\n'); - if (!insideTag) { - if (!ignoreNewline && !swallowOneNewline) - ret.append ("
    "); - swallowOneNewline = false; - } - break; - case '>': - // For Helma macro tags and comments, we overrule tag balancing, - // i.e. we don't require that '<' and '>' be balanced within - // macros and comments. Rather, we check for the matching closing tag. - if (insideComment) { - ret.append ('>'); - insideComment = !(str.charAt(i-2) == '-' && str.charAt(i-1) == '-'); - } else if (insideMacroTag) { - ret.append ('>'); - insideMacroTag = !(str.charAt(i-1) == '%' && macroQuoteChar == '\u0000'); - } else if (insideHtmlTag) { - ret.append ('>'); - // only leave HTML tag if quotation marks are balanced - // within that tag. - insideHtmlTag = htmlQuoteChar != '\u0000'; - } else { - ret.append (">"); - } - // check if we still are inside any kind of tag - insideTag = insideComment || insideMacroTag || insideHtmlTag; - break; - default: - // ret.append (c); - if (c < 128) - ret.append (c); - else if (c >= 128 && c < 256) - ret.append (transform[c-128]); - else { - ret.append ("&#"); - ret.append ((int) c); - ret.append (";"); - } - if (swallowOneNewline && !insideTag && !Character.isWhitespace (c)) - swallowOneNewline = false; - escape = false; - } - } - } + encode(str, ret, null); + return ret.toString(); + } /** - * + * Do "smart" encodging on a string. This means that valid HTML entities and tags, + * Helma macros and HTML comments are passed through unescaped, while + * other occurrences of '<', '>' and '&' are encoded to HTML entities. */ - public final static String encodeFormValue (String str) { - if (str == null) - return null; - int l = str.length(); - if (l == 0) - return ""; - StringBuffer ret = new StringBuffer (Math.round (l*1.2f)); - encodeAll (str, ret, false); - return ret.toString(); + public final static void encode(String str, StringBuffer ret) { + encode(str, ret, null); + } + + /** + * Do "smart" encodging on a string. This means that valid HTML entities and tags, + * Helma macros and HTML comments are passed through unescaped, while + * other occurrences of '<', '>' and '&' are encoded to HTML entities. + */ + public final static void encode(String str, StringBuffer ret, Set allowedTags) { + if (str == null) { + return; + } + + int l = str.length(); + + Stack openTags = new Stack(); + + // are we currently within a < and a > that consitute some kind of tag? + // we use tag balancing to know whether we are inside a tag (and should + // pass things through unchanged) or outside (and should encode stuff). + boolean insideTag = false; + + // are we inside an HTML tag? + boolean insideHtmlTag = false; + + // if we are inside a tag, we encode everything to make + // documentation work easier + boolean insideCodeTag = false; + boolean insidePreTag = false; + + // are we within a Helma <% macro %> tag? We treat macro tags and + // comments specially, since we can't rely on tag balancing + // to know when we leave a macro tag or comment. + boolean insideMacroTag = false; + + // are we inside an HTML comment? + boolean insideComment = false; + + // the quotation mark we are in within an HTML or Macro tag, if any + char htmlQuoteChar = '\u0000'; + char macroQuoteChar = '\u0000'; + + // number of newlines to ignore in \n ->
    conversion + int swallowLinebreaks = 0; + + // number of newlines met since the last non-whitespace character + int linebreaks = 0; + + // did we meet a backslash escape? + boolean escape = false; + + for (int i = 0; i < l; i++) { + char c = str.charAt(i); + + // step one: check if this is the beginning of an HTML tag, comment or + // Helma macro. + if (c == '<') { + if (i < (l - 2)) { + if (!insideMacroTag && ('%' == str.charAt(i + 1))) { + // this is the beginning of a Helma macro tag + if (!insideCodeTag) { + insideMacroTag = insideTag = true; + macroQuoteChar = '\u0000'; + } + } else if (('!' == str.charAt(i + 1)) && ('-' == str.charAt(i + 2))) { + // the beginning of an HTML comment? + if (!insideCodeTag) { + insideComment = insideTag = ((i < (l - 3)) && + ('-' == str.charAt(i + 3))); + } + } else if (!insideTag) { + // check if this is a HTML tag. + boolean insideCloseTag = ('/' == str.charAt(i + 1)); + int tagStart = insideCloseTag ? (i + 2) : (i + 1); + int j = tagStart; + + while ((j < l) && Character.isLetterOrDigit(str.charAt(j))) + j++; + + if ((j > tagStart) && (j < l)) { + String tagName = str.substring(tagStart, j).toLowerCase(); + + if ("code".equals(tagName) && insideCloseTag && + insideCodeTag) { + insideCodeTag = false; + } + + if (((allowedTags == null) || allowedTags.contains(tagName)) && + allTags.contains(tagName) && !insideCodeTag) { + insideHtmlTag = insideTag = true; + htmlQuoteChar = '\u0000'; + + // set ignoreNewline on some tags, depending on wheather they're + // being opened or closed. + // what's going on here? we switch newline encoding on inside some tags, for + // others we switch it on when they're closed + linebreaks = Math.max(linebreaks - swallowLinebreaks, 0); + + if (swallowAll.contains(tagName)) { + swallowLinebreaks = 1000; + } else if (swallowTwo.contains(tagName)) { + swallowLinebreaks = 2; + } else if (swallowOne.contains(tagName)) { + swallowLinebreaks = 1; + } else { + swallowLinebreaks = 0; + } + + if (insideCloseTag) { + int t = openTags.search(tagName); + + if (t == -1) { + i = j; + insideHtmlTag = insideTag = false; + + continue; + } else if (t > 1) { + for (int k = 1; k < t; k++) { + ret.append(""); + } + } + + openTags.pop(); + } else { + openTags.push(tagName); + swallowLinebreaks = Math.max(swallowLinebreaks - 1, 0); + } + + if ("code".equals(tagName) && !insideCloseTag) { + insideCodeTag = true; + } + + if ("pre".equals(tagName)) { + insidePreTag = !insideCloseTag; + } + } + } + } + } + // if (i < l-2) + } + + if ((linebreaks > 0) && !Character.isWhitespace(c)) { + if (!insidePreTag && (linebreaks > swallowLinebreaks)) { + linebreaks -= swallowLinebreaks; + + for (int k = 0; k < linebreaks; k++) + ret.append("
    \n"); + } + + if (!insideTag) { + swallowLinebreaks = 0; + } + + linebreaks = 0; + } + + switch (c) { + case '<': + + if (insideTag) { + ret.append('<'); + } else { + ret.append("<"); + } + + break; + + case '&': + + // check if this is an HTML entity already, + // in which case we pass it though unchanged + if ((i < (l - 3)) && !insideCodeTag) { + // is this a numeric entity? + if (str.charAt(i + 1) == '#') { + int j = i + 2; + + while ((j < l) && Character.isDigit(str.charAt(j))) + j++; + + if ((j < l) && (str.charAt(j) == ';')) { + ret.append("&"); + + break; + } + } else { + int j = i + 1; + + while ((j < l) && Character.isLetterOrDigit(str.charAt(j))) + j++; + + if ((j < l) && (str.charAt(j) == ';')) { + ret.append("&"); + + break; + } + } + } + + // we didn't reach a break, so encode the ampersand as HTML entity + ret.append("&"); + + break; + + case '\\': + ret.append(c); + + if (insideTag && !insideComment) { + escape = !escape; + } + + break; + + case '"': + case '\'': + ret.append(c); + + if (!insideComment) { + // check if the quote is escaped + if (insideMacroTag) { + if (escape) { + escape = false; + } else if (macroQuoteChar == c) { + macroQuoteChar = '\u0000'; + } else if (macroQuoteChar == '\u0000') { + macroQuoteChar = c; + } + } else if (insideHtmlTag) { + if (escape) { + escape = false; + } else if (htmlQuoteChar == c) { + htmlQuoteChar = '\u0000'; + } else if (htmlQuoteChar == '\u0000') { + htmlQuoteChar = c; + } + } + } + + break; + + case '\n': + ret.append('\n'); + + if (!insideTag) { + linebreaks++; + } + + break; + + case '>': + + // For Helma macro tags and comments, we overrule tag balancing, + // i.e. we don't require that '<' and '>' be balanced within + // macros and comments. Rather, we check for the matching closing tag. + if (insideComment) { + ret.append('>'); + insideComment = !((str.charAt(i - 2) == '-') && + (str.charAt(i - 1) == '-')); + } else if (insideMacroTag) { + ret.append('>'); + insideMacroTag = !((str.charAt(i - 1) == '%') && + (macroQuoteChar == '\u0000')); + } else if (insideHtmlTag) { + ret.append('>'); + + // only leave HTML tag if quotation marks are balanced + // within that tag. + insideHtmlTag = htmlQuoteChar != '\u0000'; + + // Check if this is an empty tag so we don't generate an + // additional tag. + if (str.charAt(i - 1) == '/') { + openTags.pop(); + } + } else { + ret.append(">"); + } + + // check if we still are inside any kind of tag + insideTag = insideComment || insideMacroTag || insideHtmlTag; + + break; + + default: + + // ret.append (c); + if (c < 128) { + ret.append(c); + } else if ((c >= 128) && (c < 256)) { + ret.append(transform[c - 128]); + } else { + ret.append("&#"); + ret.append((int) c); + ret.append(";"); + } + + escape = false; + } + } + + // if tags were opened but not closed, close them. + int o = openTags.size(); + + if (o > 0) { + for (int k = 0; k < o; k++) { + ret.append(""); + } + } + + // add remaining newlines we may have collected + if ((linebreaks > 0) && (linebreaks > swallowLinebreaks)) { + linebreaks -= swallowLinebreaks; + + for (int i = 0; i < linebreaks; i++) + ret.append("
    \n"); + } } /** * */ - public final static void encodeFormValue (String str, StringBuffer ret) { - encodeAll (str, ret, false); - } + public final static String encodeFormValue(String str) { + if (str == null) { + return null; + } + int l = str.length(); - /** - * - */ - public final static String encodeAll (String str) { - if (str == null) - return null; - int l = str.length(); - if (l == 0) - return ""; - StringBuffer ret = new StringBuffer (Math.round (l*1.2f)); - encodeAll (str, ret, true); - return ret.toString(); + if (l == 0) { + return ""; + } + + StringBuffer ret = new StringBuffer(Math.round(l * 1.2f)); + + encodeAll(str, ret, false); + + return ret.toString(); } /** * */ - public final static void encodeAll (String str, StringBuffer ret) { - encodeAll (str, ret, true); + public final static void encodeFormValue(String str, StringBuffer ret) { + encodeAll(str, ret, false); } - /** * */ - public final static void encodeAll (String str, StringBuffer ret, boolean encodeNewline) { - if (str == null) - return; + public final static String encodeAll(String str) { + if (str == null) { + return null; + } - int l = str.length(); - for (int i=0; i': - ret.append (">"); - break; - case '&': - ret.append ("&"); - break; - case '"': - ret.append ("""); - break; - case '\n': - ret.append ('\n'); - if (encodeNewline) { - ret.append ("
    "); - } - break; - default: - // ret.append (c); - if (c < 128) - ret.append (c); - else if (c >= 128 && c < 256) - ret.append (transform[c-128]); - else { - ret.append ("&#"); - ret.append ((int) c); - ret.append (";"); - } - } - } - } + int l = str.length(); + if (l == 0) { + return ""; + } - public final static String encodeXml (String str) { - if (str == null) - return null; - int l = str.length(); - if (l == 0) - return ""; - StringBuffer ret = new StringBuffer (Math.round (l*1.2f)); - encodeXml (str, ret); - return ret.toString(); + StringBuffer ret = new StringBuffer(Math.round(l * 1.2f)); + + encodeAll(str, ret, true); + + return ret.toString(); } - public final static void encodeXml (String str, StringBuffer ret) { - if (str == null) - return; + /** + * + */ + public final static void encodeAll(String str, StringBuffer ret) { + encodeAll(str, ret, true); + } - int l = str.length(); - for (int i=0; i': - ret.append (">"); - break; - case '&': - ret.append ("&"); - break; - case '"': - ret.append ("""); - break; - case '\'': - ret.append ("'"); - break; - default: - if (c < 0x20) { - // sort out invalid XML characters below 0x20 - all but 0x9, 0xA and 0xD. - // The trick is an adaption of java.lang.Character.isSpace(). - if (((((1L << 0x9) | (1L << 0xA) | (1L << 0xD)) >> c) & 1L) != 0) - ret.append (c); - } else { - ret.append (c); - } - } - } - } + /** + * + */ + public final static void encodeAll(String str, StringBuffer ret, boolean encodeNewline) { + if (str == null) { + return; + } + + int l = str.length(); + + for (int i = 0; i < l; i++) { + char c = str.charAt(i); + + switch (c) { + case '<': + ret.append("<"); + + break; + + case '>': + ret.append(">"); + + break; + + case '&': + ret.append("&"); + + break; + + case '"': + ret.append("""); + + break; + + case '\n': + ret.append('\n'); + + if (encodeNewline) { + ret.append("
    "); + } + + break; + + default: + + // ret.append (c); + if (c < 128) { + ret.append(c); + } else if ((c >= 128) && (c < 256)) { + ret.append(transform[c - 128]); + } else { + ret.append("&#"); + ret.append((int) c); + ret.append(";"); + } + } + } + } + + /** + * + * + * @param str ... + * + * @return ... + */ + public final static String encodeXml(String str) { + if (str == null) { + return null; + } + + int l = str.length(); + + if (l == 0) { + return ""; + } + + StringBuffer ret = new StringBuffer(Math.round(l * 1.2f)); + + encodeXml(str, ret); + + return ret.toString(); + } + + /** + * + * + * @param str ... + * @param ret ... + */ + public final static void encodeXml(String str, StringBuffer ret) { + if (str == null) { + return; + } + + int l = str.length(); + + for (int i = 0; i < l; i++) { + char c = str.charAt(i); + + switch (c) { + case '<': + ret.append("<"); + + break; + + case '>': + ret.append(">"); + + break; + + case '&': + ret.append("&"); + + break; + + case '"': + ret.append("""); + + break; + + case '\'': + ret.append("'"); + + break; + + default: + + if (c < 0x20) { + // sort out invalid XML characters below 0x20 - all but 0x9, 0xA and 0xD. + // The trick is an adaption of java.lang.Character.isSpace(). + if (((((1L << 0x9) | (1L << 0xA) | (1L << 0xD)) >> c) & 1L) != 0) { + ret.append(c); + } + } else { + ret.append(c); + } + } + } + } // test method - public static String printCharRange (int from, int to) { - StringBuffer response = new StringBuffer(); - for (int i=from;i= 128 && i < 256) - response.append (transform[i-128]); - else { - response.append ("&#"); - response.append (i); - response.append (";"); - } - response.append ("\r\n"); - } - return response.toString(); + public static String printCharRange(int from, int to) { + StringBuffer response = new StringBuffer(); + + for (int i = from; i < to; i++) { + response.append(i); + response.append(" "); + response.append((char) i); + response.append(" "); + + if (i < 128) { + response.append((char) i); + } else if ((i >= 128) && (i < 256)) { + response.append(transform[i - 128]); + } else { + response.append("&#"); + response.append(i); + response.append(";"); + } + + response.append("\r\n"); + } + + return response.toString(); } // for testing... - public static void main (String[] args) { - for (int i=0; i=stop; i--) { - Object o = stack.elementAt (i); - if (stoppers.contains (o)) - return; - if (until.equals (o)) { - found = i; - break; - } - } - if (found > -1) { - for (int i=l-1; i>=found; i--) { - try { - String t = (String) stack.pop (); - builder.endElement (t); - } catch (Exception x) {} - } - } + private void closeOpenTags(String until, HashSet stoppers, int maxdepth) { + int l = stack.size(); + int stop = Math.max(0, l - maxdepth); + int found = -1; + + for (int i = l - 1; i >= stop; i--) { + Object o = stack.elementAt(i); + + if (stoppers.contains(o)) { + return; + } + + if (until.equals(o)) { + found = i; + + break; + } + } + + if (found > -1) { + for (int i = l - 1; i >= found; i--) { + try { + String t = (String) stack.pop(); + + builder.endElement(t); + } catch (Exception x) { + } + } + } } class Attributes implements org.xml.sax.AttributeList { - HashMap map = new HashMap(); - ArrayList names = new ArrayList(); - ArrayList values = new ArrayList (); + HashMap map = new HashMap(); + ArrayList names = new ArrayList(); + ArrayList values = new ArrayList(); - public int getLength() { - return names.size(); - } + public int getLength() { + return names.size(); + } - public String getName (int i) { - return (String) names.get (i); - } + public String getName(int i) { + return (String) names.get(i); + } - public String getType (int i) { - return "CDATA"; - } + public String getType(int i) { + return "CDATA"; + } - public String getType (String name) { - return "CDATA"; - } + public String getType(String name) { + return "CDATA"; + } - public String getValue (int i) { - return (String) values.get (i); - } + public String getValue(int i) { + return (String) values.get(i); + } - public String getValue (String name) { - return (String) map.get (name); - } + public String getValue(String name) { + return (String) map.get(name); + } - public void convert (SimpleAttributeSet attset) { - map.clear (); - names.clear (); - values.clear (); - for (Enumeration e = attset.getAttributeNames(); e.hasMoreElements(); ) { - Object name = e.nextElement (); - Object value = attset.getAttribute (name).toString(); - name = name.toString().toLowerCase (); - map.put (name, value); - names.add (name); - values.add (value); - } - } + public void convert(SimpleAttributeSet attset) { + map.clear(); + names.clear(); + values.clear(); + for (Enumeration e = attset.getAttributeNames(); e.hasMoreElements();) { + Object name = e.nextElement(); + Object value = attset.getAttribute(name).toString(); + + name = name.toString().toLowerCase(); + map.put(name, value); + names.add(name); + values.add(value); + } + } } } - diff --git a/src/helma/util/InetAddressFilter.java b/src/helma/util/InetAddressFilter.java index 0ceaeccd..1bbc2138 100644 --- a/src/helma/util/InetAddressFilter.java +++ b/src/helma/util/InetAddressFilter.java @@ -1,5 +1,18 @@ -// InetAddressFilter.java -// Copyright (c) Hannes Wallnöfer 1998-2000 +/* + * 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; @@ -10,47 +23,109 @@ import java.util.*; /** * A class for paranoid servers to filter IP addresses. */ - public class InetAddressFilter { + private Vector patterns; - Vector patterns; + /** + * Creates a new InetAddressFilter object. + */ + public InetAddressFilter() { + patterns = new Vector(); + } - public InetAddressFilter () { - patterns = new Vector (); + /** + * Addes an address template to the address filter. + * + * @param address The string representation of the IP address, either version 4 or 6. + * + * @throws IOException if the parameter does not represent a valid IP address + */ + public void addAddress(String address) throws IOException { + boolean v6 = false; + String separator = "."; + int length = 4; + int loop = 4; + + // check if this is a v4 or v6 IP address + if (address.indexOf(":") > -1) { + v6 = true; + separator = ":."; + length = 16; + loop = 8; + } + + int[] pattern = new int[length]; + + StringTokenizer st = new StringTokenizer(address, separator); + + if (st.countTokens() != loop) { + throw new IOException("\"" + address + + "\" does not represent a valid IP address"); + } + + for (int i = 0; i < loop; i++) { + String next = st.nextToken(); + + if (v6) { + if ("*".equals(next)) { + pattern[i*2] = pattern[i*2+1] = 256; + } else if (next.length() == 0) { + pattern[i*2] = pattern[i*2+1] = 0; + } else { + int n = Integer.parseInt(next, 16); + pattern[i*2] = (byte) ((n & 0xff00) >> 8); + pattern[i*2+1] = (byte) (n & 0xff); + } + } else { + if ("*".equals(next)) { + pattern[i] = 256; + } else { + pattern[i] = (byte) Integer.parseInt(next); + } + } + } + patterns.addElement(pattern); } - - public void addAddress (String address) throws IOException { - int pattern[] = new int[4]; - StringTokenizer st = new StringTokenizer (address, "."); - if (st.countTokens () != 4) - throw new IOException ("\""+address+"\" does not represent a valid IP address"); - for (int i=0; i<4; i++) { - String next = st.nextToken (); - if ("*".equals (next)) - pattern[i] = 256; - else - pattern[i] = (byte) Integer.parseInt (next); - } - patterns.addElement (pattern); - } - - public boolean matches (InetAddress address) { - if (address == null) - return false; - byte add[] = address.getAddress (); - if (add == null) - return false; - int l = patterns.size(); - for (int k=0; k dateLastRendered) - renderDate (); - entries.add (dateCache + msg); + public void log(String msg) { + // if we are closed, drop message without further notice + if (closed) { + return; + } + + // it's enough to render the date every 5 seconds + if ((System.currentTimeMillis() - 5000) > dateLastRendered) { + renderDate(); + } + + entries.add(dateCache + msg); } - private static synchronized void renderDate () { - dateLastRendered = System.currentTimeMillis (); - dateCache = dformat.format (new Date()); + private static synchronized void renderDate() { + dateLastRendered = System.currentTimeMillis(); + dateCache = dformat.format(new Date()); } - /** * Return an object which identifies this logger. */ - public String getCanonicalName () { - return canonicalName; + public String getCanonicalName() { + return canonicalName; } - /** * Get the list of unwritten entries */ - public List getEntries () { - return entries; + public List getEntries() { + return entries; } /** * This is called by the runner thread to perform actual output. */ - public void write () { - if (entries.isEmpty ()) - return; - try { + public void write() { + if (entries.isEmpty()) { + return; + } - int l = entries.size(); + try { + int l = entries.size(); - // check if writing to printstream or file - if (out != null) { - for (int i=0; i 1000) { - // more than 1000 entries queued plus exception - something - // is definitely wrong with this logger. Write a message to std err and - // discard queued log entries. - System.err.println ("Error writing log file "+this+": "+x); - System.err.println ("Discarding "+e+" log entries."); - entries.clear (); - } - } + entries.remove(0); + out.println(entry); + } + } else { + if ((writer == null) || !logfile.exists() || !logfile.canWrite()) { + // rotate the log file if we can't write to it + rotateLogFile(); + } + + for (int i = 0; i < l; i++) { + String entry = (String) entries.get(0); + + entries.remove(0); + writer.println(entry); + } + + writer.flush(); + } + } catch (Exception x) { + int e = entries.size(); + + if (e > 1000) { + // more than 1000 entries queued plus exception - something + // is definitely wrong with this logger. Write a message to std err and + // discard queued log entries. + System.err.println("Error writing log file " + this + ": " + x); + System.err.println("Discarding " + e + " log entries."); + entries.clear(); + } + } } /** * Rotate log files, closing, renaming and gzipping the old file and * start a new one. */ - private void rotateLogFile () throws IOException { - // if the logger is not file based do nothing. - if (logfile == null) - return; - if (writer != null) try { - writer.close(); - } catch (Exception ignore) {} - // only backup/rotate if the log file is not empty, - if (logfile.exists() && logfile.length() > 0) { - String today = aformat.format(new Date()); - int ct=0; - File archive = null; - // first append just the date - String archname = filename+"-"+today+".log.gz"; - while (archive==null || archive.exists()) { - archive = new File (logdir, archname); - // for the next try we append a counter - String archidx = ct>999 ? Integer.toString(ct) : nformat.format (++ct); - archname = filename+"-"+today+"-"+ archidx +".log.gz"; - } - if (logfile.renameTo (archive)) - (new GZipper(archive)).start(); - else - System.err.println ("Error rotating log file "+canonicalName+". Old file will possibly be overwritten!"); - } - writer = new PrintWriter (new FileWriter (logfile), false); + private void rotateLogFile() throws IOException { + // if the logger is not file based do nothing. + if (logfile == null) { + return; + } + + if (writer != null) { + try { + writer.close(); + } catch (Exception ignore) { + } + } + + // only backup/rotate if the log file is not empty, + if (logfile.exists() && (logfile.length() > 0)) { + String today = aformat.format(new Date()); + int ct = 0; + File archive = null; + + // first append just the date + String archname = filename + "-" + today + ".log.gz"; + + while ((archive == null) || archive.exists()) { + archive = new File(logdir, archname); + + // for the next try we append a counter + String archidx = (ct > 999) ? Integer.toString(ct) : nformat.format(++ct); + + archname = filename + "-" + today + "-" + archidx + ".log.gz"; + } + + if (logfile.renameTo(archive)) { + (new GZipper(archive)).start(); + } else { + System.err.println("Error rotating log file " + canonicalName + + ". Old file will possibly be overwritten!"); + } + } + + writer = new PrintWriter(new FileWriter(logfile), false); } /** * Tell whether this log is closed. */ - public boolean isClosed () { - return closed; + public boolean isClosed() { + return closed; } /** * Tells a log to close down. Only the flag is set, the actual closing is * done by the runner thread next time it comes around. */ - public void close () { - this.closed = true; + public void close() { + this.closed = true; } /** * Actually closes the file writer of a log. */ - void closeFiles () { - if (writer != null) try { - writer.close (); - } catch (Exception ignore) {} + void closeFiles() { + if (writer != null) { + try { + writer.close(); + } catch (Exception ignore) { + } + } } /** * Return a string representation of this Logger */ - public String toString () { - return "Logger["+canonicalName+"]"; + public String toString() { + return "Logger[" + canonicalName + "]"; } - /** * Add a log to the list of logs and * create and start the runner thread if necessary. */ - static synchronized void start (Logger log) { - if (loggers == null) - loggers = new ArrayList (); - if (loggerMap == null) - loggerMap = new HashMap (); + static synchronized void start(Logger log) { + if (loggers == null) { + loggers = new ArrayList(); + } - loggers.add (log); - loggerMap.put (log.canonicalName, log); + if (loggerMap == null) { + loggerMap = new HashMap(); + } - if (runner == null || !runner.isAlive ()) { - runner = new Runner (); - runner.start (); - } + loggers.add(log); + loggerMap.put(log.canonicalName, log); + + if ((runner == null) || !runner.isAlive()) { + runner = new Runner(); + runner.start(); + } } /** * Return a list of all active Loggers */ - public static List getLoggers () { - if (loggers == null) - return null; - return (List) loggers.clone (); + public static List getLoggers() { + if (loggers == null) { + return null; + } + + return (List) loggers.clone(); } - /** + /** * Notify the runner thread that it should wake up and run. */ - public static void wakeup () { - if (runner != null) - runner.wakeup (); + public static void wakeup() { + if (runner != null) { + runner.wakeup(); + } } - private static void rotateAllLogs () { - int nloggers = loggers.size(); - for (int i=nloggers-1; i>=0; i--) { - Logger log = (Logger) loggers.get (i); - try { - log.rotateLogFile (); - } catch (IOException io) { - System.err.println ("Error rotating log " + log.getCanonicalName() + ": " + io.toString ()); - } - } + private static void rotateAllLogs() { + int nloggers = loggers.size(); + + for (int i = nloggers - 1; i >= 0; i--) { + Logger log = (Logger) loggers.get(i); + + try { + log.rotateLogFile(); + } catch (IOException io) { + System.err.println("Error rotating log " + log.getCanonicalName() + ": " + + io.toString()); + } + } + } + + /** + * + * + * @return ... + */ + public static long nextMidnight() { + Calendar cal = Calendar.getInstance(); + + cal.set(Calendar.DATE, 1 + cal.get(Calendar.DATE)); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 1); + + // for testing, rotate the logs every minute: + // cal.set (Calendar.MINUTE, 1 + cal.get(Calendar.MINUTE)); + return cal.getTime().getTime(); + } + + /** + * + * + * @return ... + */ + public static long lastMidnight() { + return nextMidnight() - 86400000; + } + + /** + * test main method + */ + public static void main(String[] args) throws IOException { + Logger log = new Logger(".", "testlog"); + long start = System.currentTimeMillis(); + + for (int i = 0; i < 1000; i++) { + log.log("test log entry " + i); + + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + } + } + + log.log("done: " + (System.currentTimeMillis() - start)); + System.err.println(System.currentTimeMillis() - start); + log.close(); + + // System.exit (0); } /** * The static runner class that loops through all loggers. */ static class Runner extends Thread { + public synchronized void run() { + long nextMidnight = nextMidnight(); - public synchronized void run () { - long nextMidnight = nextMidnight (); - while (runner == this && !isInterrupted ()) { - if (nextMidnight < System.currentTimeMillis ()) { - rotateAllLogs (); - nextMidnight = nextMidnight (); - } - int nloggers = loggers.size(); - for (int i=nloggers-1; i>=0; i--) { - try { - Logger log = (Logger) loggers.get (i); - log.write (); - if (log.closed && log.entries.isEmpty()) { - loggers.remove (log); - log.closeFiles (); - } - } catch (Exception x) { - System.err.println ("Error in Logger main loop: "+x); - } - } - // if there are no active logs, exit logger thread - if (loggers.size() == 0) - return; - try { - wait (250); - } catch (InterruptedException ix) {} - } - } - - public synchronized void wakeup () { - notifyAll (); - } + while ((runner == this) && !isInterrupted()) { + if (nextMidnight < System.currentTimeMillis()) { + rotateAllLogs(); + nextMidnight = nextMidnight(); + } + int nloggers = loggers.size(); + + for (int i = nloggers - 1; i >= 0; i--) { + try { + Logger log = (Logger) loggers.get(i); + + log.write(); + + if (log.closed && log.entries.isEmpty()) { + loggers.remove(log); + log.closeFiles(); + } + } catch (Exception x) { + System.err.println("Error in Logger main loop: " + x); + } + } + + // if there are no active logs, exit logger thread + if (loggers.size() == 0) { + return; + } + + try { + wait(250); + } catch (InterruptedException ix) { + } + } + } + + public synchronized void wakeup() { + notifyAll(); + } } /** * a Thread class that zips up a file, filename will stay the same. */ - class GZipper extends Thread { - File file, temp; - public GZipper (File file) { - this.file = file; - this.temp = new File (file.getAbsolutePath()+".tmp"); - } + class GZipper extends Thread { + File file; + File temp; - public void run() { - long start = System.currentTimeMillis(); - try { - GZIPOutputStream zip = new GZIPOutputStream( new FileOutputStream(temp)); - BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); - byte[] b = new byte[1024]; - int len = 0; - while( (len=in.read(b,0,1024))!=-1 ) { - zip.write(b,0,len); - } - zip.close(); - in.close(); - file.delete(); - temp.renameTo(file); - } catch ( Exception e ) { - System.err.println (e.toString()); - } - } + public GZipper(File file) { + this.file = file; + this.temp = new File(file.getAbsolutePath() + ".tmp"); + } + + public void run() { + long start = System.currentTimeMillis(); + + try { + GZIPOutputStream zip = new GZIPOutputStream(new FileOutputStream(temp)); + BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); + byte[] b = new byte[1024]; + int len = 0; + + while ((len = in.read(b, 0, 1024)) != -1) { + zip.write(b, 0, len); + } + + zip.close(); + in.close(); + file.delete(); + temp.renameTo(file); + } catch (Exception e) { + System.err.println(e.toString()); + } + } } - - - public static long nextMidnight () { - Calendar cal = Calendar.getInstance (); - cal.set (Calendar.DATE, 1 + cal.get(Calendar.DATE)); - cal.set (Calendar.HOUR_OF_DAY, 0); - cal.set (Calendar.MINUTE, 0); - cal.set (Calendar.SECOND, 1); - // for testing, rotate the logs every minute: - // cal.set (Calendar.MINUTE, 1 + cal.get(Calendar.MINUTE)); - return cal.getTime().getTime (); - } - - public static long lastMidnight () { - return nextMidnight () - 86400000; - } - - /** - * test main method - */ - public static void main (String[] args) throws IOException { - Logger log = new Logger (".", "testlog"); - long start = System.currentTimeMillis (); - for (int i=0; i<1000; i++) { - log.log ("test log entry "+i); - try { - Thread.sleep(100); - } catch (InterruptedException ie) { } - } - log.log ("done: "+(System.currentTimeMillis () - start)); - System.err.println (System.currentTimeMillis () - start); - log.close (); - // System.exit (0); - } - } diff --git a/src/helma/util/Logo.java b/src/helma/util/Logo.java index 6464b8ea..bbddbab8 100644 --- a/src/helma/util/Logo.java +++ b/src/helma/util/Logo.java @@ -1,3 +1,19 @@ +/* + * 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.io.FileInputStream; @@ -5,87 +21,151 @@ import java.io.FileInputStream; /** * class with byte data of helma logo */ +public class Logo { + public static final byte[] hop = { + 71, 73, 70, 56, 57, 97, -82, 0, 35, 0, -60, 0, 0, + -1, -1, -1, -17, -17, -17, -33, -33, -33, -52, + -52, -52, -67, -67, -67, -84, -84, -84, -103, + -103, -103, -119, -119, -119, 120, 120, 120, 102, + 102, 102, 84, 84, 84, 67, 67, 67, 51, 51, 51, 33, + 33, 33, 18, 18, 18, 0, 0, 0, -2, 1, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 33, -7, 4, 4, 20, 0, -1, 0, + 44, 0, 0, 0, 0, -82, 0, 35, 0, 0, 5, -1, 32, 32, + -114, 100, 105, -98, 104, -86, -82, 108, -21, -66, + 112, 44, -49, 116, 109, -33, 120, -82, -45, -61, + -79, 52, -114, 96, 67, 113, 24, -20, -114, -56, + -92, -78, 21, 32, 32, 22, -63, -121, 116, 74, 109, + 44, -118, -127, -91, 118, -53, 125, 5, 122, 63, 7, + 117, 76, -106, 58, -84, -40, -82, 122, -83, 28, + 24, -62, -27, -72, -4, 113, 78, 20, -78, -20, -68, + -34, 21, 40, 36, 24, 98, 115, -126, -126, 14, 12, + 8, 4, 120, 123, -118, 122, 2, 5, 10, -127, -125, + -111, -111, 117, -120, -117, -106, 92, 5, -128, + -110, -101, -100, -123, 4, -105, -96, 72, 4, 64, + -100, -91, -110, 103, 70, -95, -86, 55, 9, -112, + 83, 12, 11, 12, -111, 86, 11, -90, 100, 14, 9, 47, + 4, 4, 2, 57, 77, -107, 55, -69, -67, 36, -65, + -119, 42, 2, -69, 54, -78, 100, -97, 4, -111, 8, + 34, -74, 100, 12, 47, 82, -48, 36, -50, 15, -97, + -70, 82, -37, 54, -42, 37, -39, -37, -75, 11, 40, + 8, 82, 54, -82, 83, 34, -25, -125, -51, -46, 99, + 14, 46, -30, 37, -19, -58, 44, -19, 56, -12, 36, + -7, -47, 15, -41, 38, -56, -91, -101, 38, 34, -63, + -126, 90, 85, -96, 76, -55, 114, 96, -116, -107, + 6, 15, -84, -72, 114, 112, 80, -99, -68, 19, -55, + 70, 52, 124, 48, -128, -41, 8, 100, -34, 0, 16, + 72, -11, 17, 24, 0, 5, 15, -88, -1, -91, 64, 118, + -49, -60, -58, -114, -61, 78, -90, 28, 33, -116, + -90, 55, 49, 9, 58, -34, 27, 105, -94, 35, 9, 117, + 15, 114, 1, -64, 51, -64, 76, 1, 17, 1, 106, 53, + 40, 40, -91, -41, -80, 0, 6, -112, 66, 124, 112, + -128, 4, -54, 41, 23, 73, 24, 8, -60, 0, 79, 2, + 51, 82, -114, 2, -112, -91, 64, 68, -127, 64, 101, + -121, 34, 116, 96, 100, -86, 20, -110, 34, 32, 78, + 117, -48, -53, 64, 83, 1, 82, -86, 126, -91, 19, + 54, 110, 80, 17, 118, 57, 2, 24, 48, 87, 0, 94, + -66, 116, -122, -99, -107, -110, 86, -82, -108, + 68, 64, -85, 2, 64, -112, 10, -30, 81, 3, -48, + -20, -90, -107, 117, -111, 64, 84, 0, 5, -60, -18, + 69, -96, 96, 65, -81, 108, 102, -62, -47, 41, 0, + 81, -78, -84, 6, 8, -60, -108, 3, 0, -82, -88, 3, + -42, 15, 122, -43, -6, -13, 15, -76, -75, -112, + 67, -91, 44, 24, 61, -103, -114, 111, 109, 99, 35, + -58, 126, 80, 46, 64, 94, 118, -24, 0, -120, 65, + 32, -117, 64, 1, 41, 9, -82, 70, -75, -115, 91, + -128, -13, -120, 7, 127, 50, 19, -39, -101, -10, + 108, -118, -28, -81, 9, 23, 17, 30, 64, 123, -124, + -122, 14, -111, -57, 90, -94, 22, 52, -5, -2, -96, + -111, -99, -1, 9, 127, -83, 93, -113, 61, 17, -43, + 62, -40, 116, -13, 93, 85, 2, -11, 3, -50, -1, + 126, -5, 8, -108, -51, 23, -120, -28, 67, -113, + 127, -38, 16, 56, 2, 80, 34, 52, -12, 73, 81, 81, + 5, 32, -33, 57, 27, -126, 35, -35, 53, 14, -112, + 72, 85, 9, 27, -91, -10, 81, 95, -32, 100, 115, + 20, 56, 27, 5, 112, -104, 2, -7, 124, -91, -46, 8, + -3, -44, -13, 24, 61, -32, -44, 66, -115, -117, + -76, -11, 102, 79, 126, -13, 1, -28, 99, 113, 15, + -56, -56, 88, 62, 67, -106, -96, -114, 74, 95, + 101, 97, 87, 76, 76, -43, -43, 23, 94, 71, 97, 9, + 0, 94, 42, 61, -63, 20, 125, 35, 4, -10, 69, 95, + 27, 9, 80, -44, 3, 71, 125, -75, 84, 96, 83, 44, + 5, -111, 80, 35, -96, 116, -93, 85, 17, 1, 16, + -29, -103, 71, -31, 100, 103, 83, 120, 2, -96, + -90, 8, 103, 118, 24, -56, 103, 122, -94, -76, + -90, 67, 50, 45, 101, -126, 58, 66, 49, -96, 104, + 108, -48, -127, -39, 78, 47, -41, 81, -102, -101, + 111, 85, 57, -41, 40, 21, 89, 21, 39, 79, 96, -67, + -4, 9, 106, 114, 101, -99, -45, -23, -127, 37, + -68, 121, 2, 68, 101, -119, 122, -41, 115, -82, + 94, -70, 31, 0, -127, -91, 18, 24, 93, -121, 85, + -59, -86, -89, -87, 62, -112, -106, -109, 99, 72, + -26, 43, 123, -77, 33, -76, -98, 123, -58, 77, + 118, 17, -92, -59, -67, -93, 30, -89, 36, 36, 88, + 39, -87, -59, 41, -38, 35, -1, 115, 5, 34, 55, 2, + -86, 37, 124, -89, -33, 95, -90, 30, -9, -55, 126, + -25, 88, 91, 94, -114, 72, 90, 103, -32, -75, -77, + 33, 37, 34, -80, 84, 56, 27, -51, 121, -80, -31, + 23, 100, -79, -8, -78, 119, 41, -120, -13, -127, + -87, 47, 52, 16, 61, -5, 109, 46, 19, -10, -122, + -39, 124, 125, 32, 112, 90, 55, 38, -120, -13, 93, + 84, -46, 62, 64, -92, 44, 4, 51, -116, 108, 57, 2, + 32, 16, 85, 55, 18, 50, -116, -33, -63, 22, 94, + 56, 70, -122, -56, -99, 41, 101, 91, 104, -118, + 32, 11, -119, 44, -117, 32, -58, 81, 61, 48, 20, + 15, 9, 13, 13, -111, 82, 22, -39, 40, -16, 21, 93, + 123, 38, -71, 39, 108, 127, 121, 123, -122, 8, + -39, 32, 112, 79, 59, 6, -51, -124, 44, 115, 98, + -4, -56, -40, -50, -67, 108, 36, 108, 111, -50, + -40, 76, -51, -111, 67, -42, 124, 78, 46, 77, -62, + 43, 5, -108, 62, 7, -106, 64, 22, 3, 92, -107, + -106, 93, 89, -90, -84, -27, 97, 12, -100, -106, + -59, 94, 109, 18, -77, -13, -40, 36, 59, -102, 82, + 42, 127, -86, 69, 7, -35, -76, -54, -106, 74, 82, + 77, -107, -128, 18, 90, -61, 108, -107, -46, 110, + 123, -38, -51, 64, 42, 2, -120, -79, 84, -96, 34, + -104, -99, 69, -95, -45, 2, -114, 75, 22, -122, + -94, -32, 22, 60, -100, -113, 49, 103, 12, -86, + -22, -80, -21, 125, 42, 48, 40, -44, -7, -23, 123, + -53, 32, 0, 3, -100, -63, 101, 3, -73, -92, -69, + 112, -64, -26, -88, -37, -30, -119, 12, 3, 28, + -108, -45, 14, -71, 47, -32, 122, -20, 43, 32, 64, + 74, -19, -90, 20, 34, 22, -16, -56, -65, -48, + -120, -23, -60, 15, 66, -111, 1, 45, 37, 47, -3, + 10, -115, 40, 48, 124, -13, 85, 40, 80, 0, -107, + -45, 119, -17, -123, 19, -102, -44, 94, -56, 33, + -47, 123, 111, -2, -9, -44, 69, 81, -54, -8, -37, + -97, -17, -66, 14, 77, 60, -95, 126, 28, 20, -111, + -1, -2, -3, 109, 28, -96, 0, 32, 66, 16, 49, 64, + -7, -8, 11, -96, 0, 7, -56, -122, 16, 0, 0, 59 + }; -public class Logo { + /** + * utitilty function to create byte array from file + */ + public static void main(String[] args) throws Exception { + FileInputStream fis = new FileInputStream(args[0]); + byte[] b = new byte[256]; + int linect = 0; + int ct = 0; - public static final byte hop[] = { - 71, 73,70,56,57,97,-82,0,35,0,-60,0,0,-1,-1,-1,-17,-17,-17,-33,-33,-33,-52,-52,-52,-67, - -67,-67,-84,-84,-84,-103,-103,-103,-119,-119,-119,120,120,120,102,102,102,84,84,84,67, - 67,67,51,51,51,33,33,33,18,18,18,0,0,0,-2,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,-7,4,4,20,0,-1,0,44,0,0,0,0,-82,0, - 35,0,0,5,-1,32,32,-114,100,105,-98,104,-86,-82,108,-21,-66,112,44,-49,116,109,-33,120, - -82,-45,-61,-79,52,-114,96,67,113,24,-20,-114,-56,-92,-78,21,32,32,22,-63,-121,116,74, - 109,44,-118,-127,-91,118,-53,125,5,122,63,7,117,76,-106,58,-84,-40,-82,122,-83,28,24, - -62,-27,-72,-4,113,78,20,-78,-20,-68,-34,21,40,36,24,98,115,-126,-126,14,12,8,4,120,123, - -118,122,2,5,10,-127,-125,-111,-111,117,-120,-117,-106,92,5,-128,-110,-101,-100,-123,4, - -105,-96,72,4,64,-100,-91,-110,103,70,-95,-86,55,9,-112,83,12,11,12,-111,86,11,-90,100, - 14,9,47,4,4,2,57,77,-107,55,-69,-67,36,-65,-119,42,2,-69,54,-78,100,-97,4,-111,8,34,-74, - 100,12,47,82,-48,36,-50,15,-97,-70,82,-37,54,-42,37,-39,-37,-75,11,40,8,82,54,-82,83,34, - -25,-125,-51,-46,99,14,46,-30,37,-19,-58,44,-19,56,-12,36,-7,-47,15,-41,38,-56,-91,-101, - 38,34,-63,-126,90,85,-96,76,-55,114,96,-116,-107,6,15,-84,-72,114,112,80,-99,-68,19,-55, - 70,52,124,48,-128,-41,8,100,-34,0,16,72,-11,17,24,0,5,15,-88,-1,-91,64,118,-49,-60,-58, - -114,-61,78,-90,28,33,-116,-90,55,49,9,58,-34,27,105,-94,35,9,117,15,114,1,-64,51,-64, - 76,1,17,1,106,53,40,40,-91,-41,-80,0,6,-112,66,124,112,-128,4,-54,41,23,73,24,8,-60,0, - 79,2,51,82,-114,2,-112,-91,64,68,-127,64,101,-121,34,116,96,100,-86,20,-110,34,32,78, - 117,-48,-53,64,83,1,82,-86,126,-91,19,54,110,80,17,118,57,2,24,48,87,0,94,-66,116,-122, - -99,-107,-110,86,-82,-108,68,64,-85,2,64,-112,10,-30,81,3,-48,-20,-90,-107,117,-111,64, - 84,0,5,-60,-18,69,-96,96,65,-81,108,102,-62,-47,41,0,81,-78,-84,6,8,-60,-108,3,0,-82, - -88,3,-42,15,122,-43,-6,-13,15,-76,-75,-112,67,-91,44,24,61,-103,-114,111,109,99,35, - -58,126,80,46,64,94,118,-24,0,-120,65,32,-117,64,1,41,9,-82,70,-75,-115,91,-128,-13, - -120,7,127,50,19,-39,-101,-10,108,-118,-28,-81,9,23,17,30,64,123,-124,-122,14,-111, - -57,90,-94,22,52,-5,-2,-96,-111,-99,-1,9,127,-83,93,-113,61,17,-43,62,-40,116,-13,93, - 85,2,-11,3,-50,-1,126,-5,8,-108,-51,23,-120,-28,67,-113,127,-38,16,56,2,80,34,52,-12, - 73,81,81,5,32,-33,57,27,-126,35,-35,53,14,-112,72,85,9,27,-91,-10,81,95,-32,100,115, - 20,56,27,5,112,-104,2,-7,124,-91,-46,8,-3,-44,-13,24,61,-32,-44,66,-115,-117,-76,-11, - 102,79,126,-13,1,-28,99,113,15,-56,-56,88,62,67,-106,-96,-114,74,95,101,97,87,76,76, - -43,-43,23,94,71,97,9,0,94,42,61,-63,20,125,35,4,-10,69,95,27,9,80,-44,3,71,125,-75, - 84,96,83,44,5,-111,80,35,-96,116,-93,85,17,1,16,-29,-103,71,-31,100,103,83,120,2,-96, - -90,8,103,118,24,-56,103,122,-94,-76,-90,67,50,45,101,-126,58,66,49,-96,104,108,-48, - -127,-39,78,47,-41,81,-102,-101,111,85,57,-41,40,21,89,21,39,79,96,-67,-4,9,106,114, - 101,-99,-45,-23,-127,37,-68,121,2,68,101,-119,122,-41,115,-82,94,-70,31,0,-127,-91, - 18,24,93,-121,85,-59,-86,-89,-87,62,-112,-106,-109,99,72,-26,43,123,-77,33,-76,-98, - 123,-58,77,118,17,-92,-59,-67,-93,30,-89,36,36,88,39,-87,-59,41,-38,35,-1,115,5,34, - 55,2,-86,37,124,-89,-33,95,-90,30,-9,-55,126,-25,88,91,94,-114,72,90,103,-32,-75,-77, - 33,37,34,-80,84,56,27,-51,121,-80,-31,23,100,-79,-8,-78,119,41,-120,-13,-127,-87,47, - 52,16,61,-5,109,46,19,-10,-122,-39,124,125,32,112,90,55,38,-120,-13,93,84,-46,62,64, - -92,44,4,51,-116,108,57,2,32,16,85,55,18,50,-116,-33,-63,22,94,56,70,-122,-56,-99,41, - 101,91,104,-118,32,11,-119,44,-117,32,-58,81,61,48,20,15,9,13,13,-111,82,22,-39,40, - -16,21,93,123,38,-71,39,108,127,121,123,-122,8,-39,32,112,79,59,6,-51,-124,44,115, - 98,-4,-56,-40,-50,-67,108,36,108,111,-50,-40,76,-51,-111,67,-42,124,78,46,77,-62,43, - 5,-108,62,7,-106,64,22,3,92,-107,-106,93,89,-90,-84,-27,97,12,-100,-106,-59,94,109, - 18,-77,-13,-40,36,59,-102,82,42,127,-86,69,7,-35,-76,-54,-106,74,82,77,-107,-128,18, - 90,-61,108,-107,-46,110,123,-38,-51,64,42,2,-120,-79,84,-96,34,-104,-99,69,-95,-45,2, - -114,75,22,-122,-94,-32,22,60,-100,-113,49,103,12,-86,-22,-80,-21,125,42,48,40,-44, - -7,-23,123,-53,32,0,3,-100,-63,101,3,-73,-92,-69,112,-64,-26,-88, -37,-30,-119,12,3, - 28,-108,-45,14,-71,47,-32,122,-20,43,32,64,74,-19,-90,20,34,22,-16,-56,-65,-48,-120, - -23,-60,15,66,-111,1,45,37,47,-3,10,-115,40,48,124,-13,85,40,80,0,-107,-45,119,-17, - -123,19,-102,-44,94,-56,33,-47,123,111,-2,-9,-44,69,81,-54,-8,-37,-97,-17,-66,14,77, - 60,-95,126,28,20,-111,-1,-2,-3,109,28,-96,0,32,66,16,49,64,-7,-8,11,-96,0,7,-56,-122, - 16,0,0,59 - }; + System.out.print("\n\n\n static byte[] image = {\n "); - /** - * utitilty function to create byte array from file - */ - public static void main ( String args[] ) throws Exception { - FileInputStream fis = new FileInputStream ( args[0] ); - byte[] b = new byte[256]; - int linect=0, ct=0; - System.out.print( "\n\n\n static byte[] image = {\n "); - while ( ct>-1 ) { - ct = fis.read(b); - for ( int i=0; i30 ) { - linect=0; - System.out.print("\n "); - } - } - } - System.out.print( " };\n\n\n"); + while (ct > -1) { + ct = fis.read(b); - } + for (int i = 0; i < ct; i++) { + System.out.print(b[i] + ","); + linect++; + if (linect > 30) { + linect = 0; + System.out.print("\n "); + } + } + } + System.out.print(" };\n\n\n"); + } } diff --git a/src/helma/util/MD5Encoder.java b/src/helma/util/MD5Encoder.java index df82f7a2..ee4e5a96 100644 --- a/src/helma/util/MD5Encoder.java +++ b/src/helma/util/MD5Encoder.java @@ -1,47 +1,91 @@ +/* + * 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.security.MessageDigest; import java.security.NoSuchAlgorithmException; - +/** + * + */ public class MD5Encoder { - private static MessageDigest md; - /** used by commandline script to create admin username & password */ - public static void main ( String args[] ) throws Exception { - if ( args.length < 2 ) { - System.out.println( "\n\nUsage: helma.util.MD5Encoder "); - System.out.println( "Output:"); - System.out.println( "adminUsername="); - System.out.println( "adminPassword=\n"); + /** used by commandline script to create admin username & password */ + public static void main(String[] args) throws Exception { + if (args.length < 2) { + System.out.println("\n\nUsage: helma.util.MD5Encoder "); + System.out.println("Output:"); + System.out.println("adminUsername="); + System.out.println("adminPassword=\n"); System.exit(0); } - System.out.println( "adminUsername=" + encode(args[0]) ); - System.out.println( "adminPassword=" + encode(args[1]) ); + + System.out.println("adminUsername=" + encode(args[0])); + System.out.println("adminPassword=" + encode(args[1])); } + /** + * + * + * @param str ... + * + * @return ... + * + * @throws NoSuchAlgorithmException ... + */ public static String encode(String str) throws NoSuchAlgorithmException { - return encode (str.getBytes()); + return encode(str.getBytes()); } + /** + * + * + * @param message ... + * + * @return ... + * + * @throws NoSuchAlgorithmException ... + */ public static String encode(byte[] message) throws NoSuchAlgorithmException { md = MessageDigest.getInstance("MD5"); + byte[] b = md.digest(message); - StringBuffer buf = new StringBuffer(b.length*2); - for ( int i=0; i>>= 8; b[1] = (byte) (n); @@ -57,8 +101,7 @@ public class MD5Encoder { b[6] = (byte) (n); n >>>= 8; b[7] = (byte) (n); + return b; } - } - diff --git a/src/helma/util/MimePart.java b/src/helma/util/MimePart.java index 974c2ea4..fa18937e 100644 --- a/src/helma/util/MimePart.java +++ b/src/helma/util/MimePart.java @@ -1,5 +1,18 @@ -// MimePart.java -// Copyright (c) Hannes Wallnöfer 2001 +/* + * 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; @@ -9,83 +22,132 @@ import java.util.Date; /** * This represents a MIME part of a HTTP file upload */ - public class MimePart implements Serializable { - public final String name; public int contentLength; public String contentType; private byte[] content; - public Date lastModified; public String eTag; - - public MimePart (String name, byte[] content, String contentType) { - this.name = name; - this.content = content == null ? new byte[0] : content; - this.contentType = contentType; - contentLength = content == null ? 0 : content.length; + /** + * Creates a new MimePart object. + * + * @param name ... + * @param content ... + * @param contentType ... + */ + public MimePart(String name, byte[] content, String contentType) { + this.name = name; + this.content = (content == null) ? new byte[0] : content; + this.contentType = contentType; + contentLength = (content == null) ? 0 : content.length; } - public String getContentType () { - return contentType; + /** + * + * + * @return ... + */ + public String getContentType() { + return contentType; } - public int getContentLength () { - return contentLength; + /** + * + * + * @return ... + */ + public int getContentLength() { + return contentLength; } - public String getName () { - return name; + /** + * + * + * @return ... + */ + public String getName() { + return name; } - public byte[] getContent () { - return content; + /** + * + * + * @return ... + */ + public byte[] getContent() { + return content; } - public String getText () { - if (contentType == null || contentType.startsWith ("text/")) { - // FIXME: check for encoding - return new String (content); - } else { - return null; - } + /** + * + * + * @return ... + */ + public String getText() { + if ((contentType == null) || contentType.startsWith("text/")) { + // FIXME: check for encoding + return new String(content); + } else { + return null; + } } - - public String writeToFile (String dir) { - return writeToFile (dir, null); + /** + * + * + * @param dir ... + * + * @return ... + */ + public String writeToFile(String dir) { + return writeToFile(dir, null); } - public String writeToFile (String dir, String fname) { - try { - File base = new File (dir); - // make directories if they don't exist - if (!base.exists ()) - base.mkdirs (); + /** + * + * + * @param dir ... + * @param fname ... + * + * @return ... + */ + public String writeToFile(String dir, String fname) { + try { + File base = new File(dir); - String filename = name; - if (fname != null) { - if (fname.indexOf (".") < 0) { - // check if we can use extension from name - int ndot = name == null ? -1 : name.lastIndexOf ("."); - if (ndot > -1) - filename = fname + name.substring (ndot); - else - filename = fname; - } else { - filename = fname; - } - } - File file = new File (base, filename); - FileOutputStream fout = new FileOutputStream (file); - fout.write (getContent ()); - fout.close (); - return filename; - } catch (Exception x) { - return null; - } + // make directories if they don't exist + if (!base.exists()) { + base.mkdirs(); + } + + String filename = name; + + if (fname != null) { + if (fname.indexOf(".") < 0) { + // check if we can use extension from name + int ndot = (name == null) ? (-1) : name.lastIndexOf("."); + + if (ndot > -1) { + filename = fname + name.substring(ndot); + } else { + filename = fname; + } + } else { + filename = fname; + } + } + + File file = new File(base, filename); + FileOutputStream fout = new FileOutputStream(file); + + fout.write(getContent()); + fout.close(); + + return filename; + } catch (Exception x) { + return null; + } } - } diff --git a/src/helma/util/MimePartDataSource.java b/src/helma/util/MimePartDataSource.java index a120afe0..7cd7fac7 100644 --- a/src/helma/util/MimePartDataSource.java +++ b/src/helma/util/MimePartDataSource.java @@ -1,45 +1,89 @@ -// MimePartDataSource.java -// Copyright (c) Hannes Wallnöfer 1999-2000 +/* + * 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 javax.activation.*; import java.io.*; +import javax.activation.*; /** * Makes MimeParts usable as Datasources in the Java Activation Framework (JAF) */ - public class MimePartDataSource implements DataSource { - private MimePart part; private String name; - public MimePartDataSource (MimePart part) { + /** + * Creates a new MimePartDataSource object. + * + * @param part ... + */ + public MimePartDataSource(MimePart part) { this.part = part; - this.name = part.getName (); + this.name = part.getName(); } - public MimePartDataSource (MimePart part, String name) { + /** + * Creates a new MimePartDataSource object. + * + * @param part ... + * @param name ... + */ + public MimePartDataSource(MimePart part, String name) { this.part = part; this.name = name; } - + /** + * + * + * @return ... + * + * @throws IOException ... + */ public InputStream getInputStream() throws IOException { - return new ByteArrayInputStream(part.getContent ()); + return new ByteArrayInputStream(part.getContent()); } - public OutputStream getOutputStream () throws IOException { - throw new IOException ("Can't write to MimePart object."); + /** + * + * + * @return ... + * + * @throws IOException ... + */ + public OutputStream getOutputStream() throws IOException { + throw new IOException("Can't write to MimePart object."); } + /** + * + * + * @return ... + */ public String getContentType() { return part.contentType; } - public String getName () { + /** + * + * + * @return ... + */ + public String getName() { return name; } - } diff --git a/src/helma/util/ParanoidServerSocket.java b/src/helma/util/ParanoidServerSocket.java index b3955bf5..30044d86 100644 --- a/src/helma/util/ParanoidServerSocket.java +++ b/src/helma/util/ParanoidServerSocket.java @@ -1,50 +1,98 @@ -// ParanoidServerSocket.java -// Copyright (c) Hannes Wallnöfer 1999-2000 +/* + * 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.net.*; import java.io.IOException; +import java.net.*; /** * A server socket that can allow connections to only a few selected hosts. */ - public class ParanoidServerSocket extends ServerSocket { - private InetAddressFilter filter; - - public ParanoidServerSocket (int port) throws IOException { - super (port); + /** + * Creates a new ParanoidServerSocket object. + * + * @param port ... + * + * @throws IOException ... + */ + public ParanoidServerSocket(int port) throws IOException { + super(port); } - public ParanoidServerSocket (int port, InetAddressFilter filter) throws IOException { - super (port); - this.filter = filter; + /** + * Creates a new ParanoidServerSocket object. + * + * @param port ... + * @param filter ... + * + * @throws IOException ... + */ + public ParanoidServerSocket(int port, InetAddressFilter filter) + throws IOException { + super(port); + this.filter = filter; } - public Socket accept () throws IOException { - Socket s = null; - while (s == null) { - s = super.accept (); - if (filter != null && !filter.matches (s.getInetAddress ())) { - System.err.println ("Refusing connection from "+s.getInetAddress ()); - try { - s.close(); - } catch (IOException ignore) {} - s = null; - } - } - return s; + /** + * + * + * @return ... + * + * @throws IOException ... + */ + public Socket accept() throws IOException { + Socket s = null; + + while (s == null) { + s = super.accept(); + + if ((filter != null) && !filter.matches(s.getInetAddress())) { + System.err.println("Refusing connection from " + s.getInetAddress()); + + try { + s.close(); + } catch (IOException ignore) { + } + + s = null; + } + } + + return s; } - public void setFilter (InetAddressFilter filter) { - this.filter = filter; + /** + * + * + * @param filter ... + */ + public void setFilter(InetAddressFilter filter) { + this.filter = filter; } - public InetAddressFilter getFilter () { - return this.filter; + /** + * + * + * @return ... + */ + public InetAddressFilter getFilter() { + return this.filter; } - } diff --git a/src/helma/util/SystemProperties.java b/src/helma/util/SystemProperties.java index 259fa67f..408ab729 100644 --- a/src/helma/util/SystemProperties.java +++ b/src/helma/util/SystemProperties.java @@ -1,25 +1,35 @@ -// Property.java -// Copyright (c) Hannes Wallnöfer 1997-2000 - +/* + * 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.*; import java.io.*; +import java.util.*; /** * A property dictionary that is updated from a property file each time the * file is modified. It is also case insensitive. */ - public final class SystemProperties extends Properties { - - private SystemProperties defaultProps; // the default/fallback properties. - private File file; // the underlying properties file from which we read. - private long lastread, lastcheck, lastadd; // time we last read/checked the underlying properties file - - // the timespan for which we omit checking for changed files after we - // did a check, in milliseconds. - final static long cacheTime = 1500l; + final static long cacheTime = 1500L; + private SystemProperties defaultProps; // the default/fallback properties. + private File file; // the underlying properties file from which we read. + private long lastread; // time we last read/checked the underlying properties file + private long lastcheck; // time we last read/checked the underlying properties file + private long lastadd; // time we last read/checked the underlying properties file // map of additional properties private HashMap additionalProps = null; @@ -27,253 +37,296 @@ public final class SystemProperties extends Properties { /** * Construct an empty properties object. */ - public SystemProperties () { - this (null, null); + public SystemProperties() { + this(null, null); } /** * Construct a properties object and read it from an input stream. */ - public SystemProperties (InputStream in) { - this (null, null); - try { - load (in); - } catch (Exception x) { - System.err.println ("Error reading properties from file "+file+": "+x); - } finally { - try { - in.close (); - } catch (Exception ignore) {} - } - lastread = System.currentTimeMillis (); + public SystemProperties(InputStream in) { + this(null, null); + + try { + load(in); + } catch (Exception x) { + System.err.println("Error reading properties from file " + file + ": " + x); + } finally { + try { + in.close(); + } catch (Exception ignore) { + } + } + + lastread = System.currentTimeMillis(); } /** * Construct a properties object from a properties file. */ - public SystemProperties (String filename) { - this (filename, null); + public SystemProperties(String filename) { + this(filename, null); } /** * Contstruct a properties object with the given default properties. */ - public SystemProperties (SystemProperties defaultProps) { - this (null, defaultProps); + public SystemProperties(SystemProperties defaultProps) { + this(null, defaultProps); } - /** * Construct a properties object from a file name with the given default properties */ - public SystemProperties (String filename, SystemProperties defaultProps) { - // System.err.println ("building sysprops with file "+filename+" and node "+node); - super (defaultProps); - this.defaultProps = defaultProps; - file = filename == null ? null : new File (filename); - lastcheck = lastread = lastadd = 0; + public SystemProperties(String filename, SystemProperties defaultProps) { + // System.err.println ("building sysprops with file "+filename+" and node "+node); + super(defaultProps); + this.defaultProps = defaultProps; + file = (filename == null) ? null : new File(filename); + lastcheck = lastread = lastadd = 0; } - /** - * Return the modify-time of the underlying properties file. - */ - public long lastModified () { - if (file == null || !file.exists ()) - return lastadd; - return Math.max (file.lastModified (), lastadd); + /** + * Return the modify-time of the underlying properties file. + */ + public long lastModified() { + if ((file == null) || !file.exists()) { + return lastadd; + } + + return Math.max(file.lastModified(), lastadd); } /** * Return a checksum that changes when something in the properties changes. */ - public long getChecksum () { - if (defaultProps == null) - return lastModified (); - return lastModified () + defaultProps.lastModified (); + public long getChecksum() { + if (defaultProps == null) { + return lastModified(); + } + + return lastModified() + defaultProps.lastModified(); } /** * Private method to read file if it has been changed since the last time we did */ - private void checkFile () { - if (file != null && file.exists() && file.lastModified () > lastread) - readFile (); - lastcheck = System.currentTimeMillis (); + private void checkFile() { + if ((file != null) && file.exists() && (file.lastModified() > lastread)) { + readFile(); + } + + lastcheck = System.currentTimeMillis(); } /** * Private method to read the underlying properties file. Assumes that the * file exists and is readable. */ - private synchronized void readFile () { - // IServer.getLogger().log ("Reading properties from file "+file); - FileInputStream bpin = null; - try { - bpin = new FileInputStream (file); - load (bpin); - } catch (Exception x) { - System.err.println ("Error reading properties from file "+file+": "+x); - } finally { - try { - bpin.close (); - } catch (Exception ignore) {} - } + private synchronized void readFile() { + // IServer.getLogger().log ("Reading properties from file "+file); + FileInputStream bpin = null; + + try { + bpin = new FileInputStream(file); + load(bpin); + } catch (Exception x) { + System.err.println("Error reading properties from file " + file + ": " + x); + } finally { + try { + bpin.close(); + } catch (Exception ignore) { + } + } } + /** + * + * + * @param in ... + * + * @throws IOException ... + */ + public synchronized void load(InputStream in) throws IOException { + clear(); + super.load(in); - public synchronized void load (InputStream in) throws IOException { - clear (); - super.load (in); - if (additionalProps != null) { - for (Iterator i=additionalProps.values().iterator(); i.hasNext(); ) - putAll ((Properties) i.next()); - } - lastread = System.currentTimeMillis (); + if (additionalProps != null) { + for (Iterator i = additionalProps.values().iterator(); i.hasNext();) + putAll((Properties) i.next()); + } + + lastread = System.currentTimeMillis(); } - /** * Similar to load(), but adds to the existing properties instead * of discarding them. */ - public synchronized void addProps (String key, InputStream in) throws IOException { - Properties p = new SystemProperties(); - p.load (in); - if (additionalProps == null) - additionalProps = new HashMap (); - additionalProps.put (key, p); - putAll (p); - lastadd = System.currentTimeMillis (); + public synchronized void addProps(String key, InputStream in) + throws IOException { + Properties p = new SystemProperties(); + + p.load(in); + + if (additionalProps == null) { + additionalProps = new HashMap(); + } + + additionalProps.put(key, p); + putAll(p); + lastadd = System.currentTimeMillis(); } - /** + /** * Remove an additional properties dictionary. */ - public synchronized void removeProps (String key) { - if (additionalProps != null) { - // remove added properties for this key. If we had - // properties associated with the key, mark props as updated. - Object p = additionalProps.remove (key); - if (p != null) - lastadd = System.currentTimeMillis (); - } - } + public synchronized void removeProps(String key) { + if (additionalProps != null) { + // remove added properties for this key. If we had + // properties associated with the key, mark props as updated. + Object p = additionalProps.remove(key); + if (p != null) { + lastadd = System.currentTimeMillis(); + } + } + } /* * This should not be used directly if properties are read from file, * otherwise changes will be lost whe the file is next modified. */ - public Object put (Object key, Object value) { - // cut off trailing whitespace - if (value != null) - value = value.toString().trim(); - return super.put (key.toString().toLowerCase(), value); + public Object put(Object key, Object value) { + // cut off trailing whitespace + if (value != null) { + value = value.toString().trim(); + } + + return super.put(key.toString().toLowerCase(), value); } /** * Overrides method to act on the wrapped properties object. */ - public Object get (Object key) { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.get (key.toString().toLowerCase()); + public Object get(Object key) { + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.get(key.toString().toLowerCase()); } /** * Overrides method to act on the wrapped properties object. */ - public Object remove (Object key) { - return super.remove (key.toString().toLowerCase()); + public Object remove(Object key) { + return super.remove(key.toString().toLowerCase()); } /** * Overrides method to act on the wrapped properties object. */ - public boolean contains (Object obj) { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.contains (obj); + public boolean contains(Object obj) { + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.contains(obj); } /** * Overrides method to act on the wrapped properties object. */ - public boolean containsKey (Object key) { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.containsKey (key.toString().toLowerCase()); + public boolean containsKey(Object key) { + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.containsKey(key.toString().toLowerCase()); } /** * Overrides method to act on the wrapped properties object. */ - public boolean isEmpty () { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.isEmpty (); + public boolean isEmpty() { + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.isEmpty(); } /** * Overrides method to act on the wrapped properties object. */ - public String getProperty (String name) { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.getProperty (name.toLowerCase()); + public String getProperty(String name) { + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.getProperty(name.toLowerCase()); } /** * Overrides method to act on the wrapped properties object. */ - public String getProperty (String name, String defaultValue) { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.getProperty (name.toLowerCase(), defaultValue); + public String getProperty(String name, String defaultValue) { + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.getProperty(name.toLowerCase(), defaultValue); } /** * Overrides method to act on the wrapped properties object. */ - public Enumeration keys () { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.keys(); + public Enumeration keys() { + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.keys(); } /** * Overrides method to act on the wrapped properties object. */ - public Set keySet () { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.keySet(); + public Set keySet() { + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.keySet(); } /** * Overrides method to act on the wrapped properties object. */ - public Enumeration elements () { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.elements(); + public Enumeration elements() { + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.elements(); } /** * Overrides method to act on the wrapped properties object. */ public int size() { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return super.size(); + if ((System.currentTimeMillis() - lastcheck) > cacheTime) { + checkFile(); + } + + return super.size(); } /** * Overrides method to act on the wrapped properties object. */ - public String toString () { - return super.toString (); + public String toString() { + return super.toString(); } - } - diff --git a/src/helma/util/Timer.java b/src/helma/util/Timer.java index 3d59ecb7..c59a5ee5 100644 --- a/src/helma/util/Timer.java +++ b/src/helma/util/Timer.java @@ -1,70 +1,106 @@ -// Timer.java -// Copyright (c) Hannes Wallnöfer 1999-2000 +/* + * 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.*; import java.io.PrintStream; +import java.util.*; /** * Utility class for timing a series of events */ - public class Timer { - private Vector timeline; private Hashtable events; - public Timer () { - timeline = new Vector (); - events = new Hashtable (); + /** + * Creates a new Timer object. + */ + public Timer() { + timeline = new Vector(); + events = new Hashtable(); } - public void reset () { - timeline.setSize (0); - events.clear (); + /** + * + */ + public void reset() { + timeline.setSize(0); + events.clear(); } - public void beginEvent (String name) { - timeline.addElement (name); - events.put (name, new Event (name)); + /** + * + * + * @param name ... + */ + public void beginEvent(String name) { + timeline.addElement(name); + events.put(name, new Event(name)); } - public void endEvent (String name) { - Event event = (Event) events.get (name); - if (event != null) - event.terminate (); + /** + * + * + * @param name ... + */ + public void endEvent(String name) { + Event event = (Event) events.get(name); + + if (event != null) { + event.terminate(); + } } - public void dump (PrintStream out) { - for (int i=0; i='a' && c<='z' || - c>='A' && c<='Z' || - c>='0' && c<='9')) { - return URLEncoder.encode (str); + } else if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || + ((c >= '0') && (c <= '9')))) { + return URLEncoder.encode(str); } } - if (needsSpaceEncoding) - return str.replace (' ', '+'); + + if (needsSpaceEncoding) { + return str.replace(' ', '+'); + } + return str; } - -} \ No newline at end of file +} diff --git a/src/helma/util/WebBroadcaster.java b/src/helma/util/WebBroadcaster.java index 48eb09ef..8fe891d4 100644 --- a/src/helma/util/WebBroadcaster.java +++ b/src/helma/util/WebBroadcaster.java @@ -1,5 +1,18 @@ -// WebBroadcaster.java -// Copyright (c) Hannes Wallnöfer 1999-2000 +/* + * 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; @@ -10,341 +23,399 @@ import java.util.*; /** * A utility hack to do "html web broadcasts". */ - public class WebBroadcaster implements Runnable { + static String lastResult = ""; + static String reloadJS = "\r\n\r\n"; + static String scrollJS = "\r\n\r\n\r\n"; + private Vector connections; + private ServerSocket serverSocket; + private Thread listener; + private boolean paranoid; + private Vector accept; + private Vector deny; + long time; + int last; - private Vector connections; - private ServerSocket serverSocket; - private Thread listener; - private boolean paranoid; - static String lastResult = ""; - private Vector accept, deny; - static String reloadJS = "\r\n\r\n"; - static String scrollJS = "\r\n\r\n\r\n"; - long time; - int last; + /** + * Creates a Web server at the specified port number. + */ + public WebBroadcaster(int port) throws IOException { + super(); + connections = new Vector(); + accept = new Vector(); + deny = new Vector(); - /** - * - */ - public static void main (String args[]) { - System.out.println ("Usage: java helma.util.WebBroadcaster [port]"); - int p = 8080; - if (args.length > 0) try { - p = Integer.parseInt (args[0]); - } catch (NumberFormatException nfx) { - System.out.println ("Error parsing port number: "+args[0]); - } - - try { - WebBroadcaster server = new WebBroadcaster (p); - // webserver.setParanoid (false); - // webserver.acceptClient ("192.168.*.*"); - System.out.println ("started web broadcast server on port "+p); - } catch (IOException x) { - System.out.println ("Error creating web broadcast server: "+x); + // make a new server socket with extra large queue size + this.serverSocket = new ServerSocket(port, 2000); + listener = new Thread(this); + listener.start(); } - } + /** + * + */ + public static void main(String[] args) { + System.out.println("Usage: java helma.util.WebBroadcaster [port]"); + int p = 8080; - /** - * Creates a Web server at the specified port number. - */ - public WebBroadcaster (int port) throws IOException { - super(); - connections = new Vector (); - accept = new Vector (); - deny = new Vector (); - // make a new server socket with extra large queue size - this.serverSocket = new ServerSocket (port, 2000); - listener = new Thread (this); - listener.start (); - } + if (args.length > 0) { + try { + p = Integer.parseInt(args[0]); + } catch (NumberFormatException nfx) { + System.out.println("Error parsing port number: " + args[0]); + } + } + try { + WebBroadcaster server = new WebBroadcaster(p); - public void broadcast (String message) { - long start = System.currentTimeMillis (); - int l = connections.size (); - synchronized (this) { - if (l != last) { - System.out.println ("broadcasting to "+l+" clients in "+time+" millis."); - last = l; - } + // webserver.setParanoid (false); + // webserver.acceptClient ("192.168.*.*"); + System.out.println("started web broadcast server on port " + p); + } catch (IOException x) { + System.out.println("Error creating web broadcast server: " + x); + } } - for (int i=l-1; i>=0; i--) { - try { - Connection c = (Connection) connections.elementAt (i); - c.send (message); - } catch (Exception ignore) {} + + /** + * + * + * @param message ... + */ + public void broadcast(String message) { + long start = System.currentTimeMillis(); + int l = connections.size(); + + synchronized (this) { + if (l != last) { + System.out.println("broadcasting to " + l + " clients in " + time + + " millis."); + last = l; + } + } + + for (int i = l - 1; i >= 0; i--) { + try { + Connection c = (Connection) connections.elementAt(i); + + c.send(message); + } catch (Exception ignore) { + } + } + + time = System.currentTimeMillis() - start; } - time = System.currentTimeMillis () - start; - } - - /** - * Switch client filtering on/off. - * @see acceptClient(java.lang.String) - * @see denyClient(java.lang.String) - */ - public void setParanoid (boolean p) { - paranoid = p; - } - - - /** - * Add an IP address to the list of accepted clients. The parameter can contain '*' as wildcard - * character, e.g. "192.168.*.*". You must call setParanoid(true) in order for this to have any - * effect. - * - * @see denyClient(java.lang.String) - * @see setParanoid(boolean) - */ - public void acceptClient (String address) throws IllegalArgumentException { - try { - AddressMatcher m = new AddressMatcher (address); - accept.addElement (m); - } catch (Exception x) { - throw new IllegalArgumentException ("\""+address+"\" does not represent a valid IP address"); + /** + * Switch client filtering on/off. + * @see acceptClient(java.lang.String) + * @see denyClient(java.lang.String) + */ + public void setParanoid(boolean p) { + paranoid = p; } - } - /** - * Add an IP address to the list of denied clients. The parameter can contain '*' as wildcard - * character, e.g. "192.168.*.*". You must call setParanoid(true) in order for this to have any - * effect. - * - * @see acceptClient(java.lang.String) - * @see setParanoid(boolean) - */ - public void denyClient (String address) throws IllegalArgumentException { - try { - AddressMatcher m = new AddressMatcher (address); - deny.addElement (m); - } catch (Exception x) { - throw new IllegalArgumentException ("\""+address+"\" does not represent a valid IP address"); + /** + * Add an IP address to the list of accepted clients. The parameter can contain '*' as wildcard + * character, e.g. "192.168.*.*". You must call setParanoid(true) in order for this to have any + * effect. + * + * @see denyClient(java.lang.String) + * @see setParanoid(boolean) + */ + public void acceptClient(String address) throws IllegalArgumentException { + try { + AddressMatcher m = new AddressMatcher(address); + + accept.addElement(m); + } catch (Exception x) { + throw new IllegalArgumentException("\"" + address + + "\" does not represent a valid IP address"); + } } - } - - private boolean checkSocket (Socket s) { - int l = deny.size (); - byte address[] = s.getInetAddress ().getAddress (); - for (int i=0; i 0) System.out.println ("Reusing connection: "+cycle); - String line = reader.readLine(); - if (line == null) throw new IOException ("connection reset"); - int contentLength = 0; - StringTokenizer tokens = new StringTokenizer(line); - String method = tokens.nextToken(); - String uri = tokens.nextToken (); - String httpversion = tokens.nextToken (); - keepalive = "HTTP/1.1".equals (httpversion); - - do { - // System.out.println (line); - line = reader.readLine(); - if (line != null) { - line = line.toLowerCase (); - if (line.startsWith ("content-length:")) - contentLength = Integer.parseInt (line.substring (15).trim ()); - if (line.startsWith ("connection:")) - keepalive = line.indexOf ("keep-alive") > -1; - } - } while (line != null && ! line.equals("")); - // System.out.println (""); - - if ("GET".equals (method)) { - - output.write (httpversion+" 200 OK\r\n"); - output.write ("Server: helma.WebBroadcast\r\n"); - output.write ("Content-Type: text/html\r\n"); - newConnection = uri.startsWith ("/NEW"); - if (!newConnection) { - output.write ("Content-Length: 5\r\n"); - if (keepalive) - output.write ("Connection: keep-alive\r\n"); - output.write ("\r\n"); - output.write ("done."); - output.flush (); - cycle += 1; - if (uri.startsWith ("/MSG")) - broadcast (uri+"
    \r\n"); - continue; - } - - output.write ("Connection: close\r\n"); - output.write ("\r\n"); - output.write (reloadJS); - output.write (scrollJS); - output.flush (); - - connections.addElement (this); - - } else { - output.write ("HTTP/1.0 400 Bad Request\r\n"); - output.write ("Server: helma.WebBroadcast\r\n\r\n"); - output.write ("Bad Request."); - // output.write (lastResult); - output.flush (); - keepalive = false; + public Connection(Socket socket) throws IOException { + super(); + this.socket = socket; + socket.setSoTimeout(30000); + input = new BufferedInputStream(socket.getInputStream()); + output = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); + responder = new Thread(this); + responder.start(); + } + + /* public void run () { + Thread current = Thread.currentThread (); + if (current == cleaner) { + cleanup (); + } else if (current == responder) { + respond (); + } + } */ + public void run() { + boolean newConnection = false; + + try { + DataInputStream reader = new DataInputStream(input); + boolean keepalive = false; + int cycle = 0; + + // implement keep-alive connections + do { + // if (cycle > 0) System.out.println ("Reusing connection: "+cycle); + String line = reader.readLine(); + + if (line == null) { + throw new IOException("connection reset"); + } + + int contentLength = 0; + StringTokenizer tokens = new StringTokenizer(line); + String method = tokens.nextToken(); + String uri = tokens.nextToken(); + String httpversion = tokens.nextToken(); + + keepalive = "HTTP/1.1".equals(httpversion); + + do { + // System.out.println (line); + line = reader.readLine(); + + if (line != null) { + line = line.toLowerCase(); + + if (line.startsWith("content-length:")) { + contentLength = Integer.parseInt(line.substring(15).trim()); + } + + if (line.startsWith("connection:")) { + keepalive = line.indexOf("keep-alive") > -1; + } + } + } while ((line != null) && !line.equals("")); + + // System.out.println (""); + if ("GET".equals(method)) { + output.write(httpversion + " 200 OK\r\n"); + output.write("Server: helma.WebBroadcast\r\n"); + output.write("Content-Type: text/html\r\n"); + newConnection = uri.startsWith("/NEW"); + + if (!newConnection) { + output.write("Content-Length: 5\r\n"); + + if (keepalive) { + output.write("Connection: keep-alive\r\n"); + } + + output.write("\r\n"); + output.write("done."); + output.flush(); + cycle += 1; + + if (uri.startsWith("/MSG")) { + broadcast(uri + "
    \r\n"); + } + + continue; + } + + output.write("Connection: close\r\n"); + output.write("\r\n"); + output.write(reloadJS); + output.write(scrollJS); + output.flush(); + + connections.addElement(this); + } else { + output.write("HTTP/1.0 400 Bad Request\r\n"); + output.write("Server: helma.WebBroadcast\r\n\r\n"); + output.write("Bad Request."); + + // output.write (lastResult); + output.flush(); + keepalive = false; + } + } while (keepalive && !newConnection); + } catch (Exception x) { + System.out.print("."); + } finally { + if (newConnection) { // leave connection open + + return; + } + + try { + output.close(); + } catch (IOException ignore) { + } + + try { + input.close(); + } catch (IOException ignore) { + } + + try { + socket.close(); + } catch (IOException ignore) { + } + } + } + + public void cleanup() { + } + + public synchronized void send(String message) { + try { + output.write(message); + output.flush(); + } catch (Exception exception) { + try { + connections.removeElement(this); + } catch (Exception ignore) { + } + + try { + output.close(); + } catch (IOException ignore) { + } + + try { + input.close(); + } catch (IOException ignore) { + } + + try { + socket.close(); + } catch (IOException ignore) { + } + } + } + + public String toString() { + return socket.getInetAddress().getHostName(); } - } while (keepalive && !newConnection); - } catch (Exception x) { - System.out.print ("."); - } finally { - if (newConnection) // leave connection open - return; - try { - output.close(); - } catch (IOException ignore) {} - try { - input.close(); - } catch (IOException ignore) {} - try { - socket.close(); - } catch (IOException ignore) {} } - } - - public void cleanup () { - - } - - public synchronized void send (String message) { - try { - output.write (message); - output.flush (); - } catch (Exception exception) { - try { - connections.removeElement (this); - } catch (Exception ignore) {} - try { - output.close(); - } catch (IOException ignore) {} - try { - input.close(); - } catch (IOException ignore) {} - try { - socket.close(); - } catch (IOException ignore) {} - } - } + class AddressMatcher { + int[] pattern; - public String toString () { - return socket.getInetAddress ().getHostName (); - } + public AddressMatcher(String address) throws Exception { + pattern = new int[4]; -} + StringTokenizer st = new StringTokenizer(address, "."); + if (st.countTokens() != 4) { + throw new Exception("\"" + address + + "\" does not represent a valid IP address"); + } -class AddressMatcher { + for (int i = 0; i < 4; i++) { + String next = st.nextToken(); - int pattern[]; - - public AddressMatcher (String address) throws Exception { - pattern = new int[4]; - StringTokenizer st = new StringTokenizer (address, "."); - if (st.countTokens () != 4) - throw new Exception ("\""+address+"\" does not represent a valid IP address"); - for (int i=0; i<4; i++) { - String next = st.nextToken (); - if ("*".equals (next)) - pattern[i] = 256; - else - pattern[i] = (byte) Integer.parseInt (next); - } - } - - public boolean matches (byte address[]) { - for (int i=0; i<4; i++) { - if (pattern[i] > 255) // wildcard - continue; - if (pattern[i] != address[i]) - return false; - } - return true; + if ("*".equals(next)) { + pattern[i] = 256; + } else { + pattern[i] = (byte) Integer.parseInt(next); + } + } + } + + public boolean matches(byte[] address) { + for (int i = 0; i < 4; i++) { + if (pattern[i] > 255) { // wildcard + + continue; + } + + if (pattern[i] != address[i]) { + return false; + } + } + + return true; + } } } - -} \ No newline at end of file diff --git a/src/helma/util/XmlUtils.java b/src/helma/util/XmlUtils.java index 918ebcb1..e98e96a3 100644 --- a/src/helma/util/XmlUtils.java +++ b/src/helma/util/XmlUtils.java @@ -1,78 +1,129 @@ +/* + * 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.io.InputStream; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.Parser; +import org.xml.sax.SAXException; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; -import java.io.InputStreamReader; -import java.net.URL; import java.net.MalformedURLException; - -import javax.xml.parsers.DocumentBuilderFactory; +import java.net.URL; import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.SAXParserFactory; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.ParserConfigurationException; - -import org.w3c.dom.Document; -import org.xml.sax.Parser; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - +import javax.xml.parsers.SAXParserFactory; +/** + * + */ public class XmlUtils { - private static DocumentBuilderFactory domBuilderFactory = null; private static SAXParserFactory saxParserFactory = null; - public static Document parseXml (Object obj) - throws SAXException, IOException, ParserConfigurationException { - if (domBuilderFactory == null) - domBuilderFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance(); - DocumentBuilder parser = domBuilderFactory.newDocumentBuilder(); - Document doc = null; - if (obj instanceof String) try { - // first try to interpret string as URL - URL url = new URL (obj.toString ()); - doc = parser.parse (obj.toString()); - } catch (MalformedURLException nourl) { - // if not a URL, maybe it is the XML itself - doc = parser.parse (new InputSource (new StringReader (obj.toString()))); - } else if (obj instanceof InputStream) { - doc = parser.parse (new InputSource ((InputStream) obj)); - } else if (obj instanceof Reader) { - doc = parser.parse (new InputSource ((Reader) obj)); - } - doc.normalize(); - return doc; + /** + * + * + * @param obj ... + * + * @return ... + * + * @throws SAXException ... + * @throws IOException ... + * @throws ParserConfigurationException ... + */ + public static Document parseXml(Object obj) + throws SAXException, IOException, + ParserConfigurationException { + if (domBuilderFactory == null) { + domBuilderFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance(); + } + + DocumentBuilder parser = domBuilderFactory.newDocumentBuilder(); + Document doc = null; + + if (obj instanceof String) { + try { + // first try to interpret string as URL + URL url = new URL(obj.toString()); + + doc = parser.parse(obj.toString()); + } catch (MalformedURLException nourl) { + // if not a URL, maybe it is the XML itself + doc = parser.parse(new InputSource(new StringReader(obj.toString()))); + } + } else if (obj instanceof InputStream) { + doc = parser.parse(new InputSource((InputStream) obj)); + } else if (obj instanceof Reader) { + doc = parser.parse(new InputSource((Reader) obj)); + } + + doc.normalize(); + + return doc; } + /** + * + * + * @param obj ... + * + * @return ... + * + * @throws SAXException ... + * @throws IOException ... + * @throws ParserConfigurationException ... + */ + public static Document parseHtml(Object obj) + throws SAXException, IOException, + ParserConfigurationException { + try { + Class.forName("org.apache.html.dom.HTMLBuilder"); + } catch (Throwable notfound) { + throw new IOException("Couldn't load nekohtml/Xerces HTML parser: " + + notfound); + } - public static Document parseHtml (Object obj) - throws SAXException, IOException, ParserConfigurationException { - try { - Class.forName ("org.apache.html.dom.HTMLBuilder"); - } catch (Throwable notfound) { - throw new IOException ("Couldn't load nekohtml/Xerces HTML parser: "+notfound); - } - Document doc = null; - HtmlParser parser = new HtmlParser (); - if (obj instanceof String) try { - // first try to interpret string as URL - URL url = new URL (obj.toString ()); - parser.parse (new InputStreamReader (url.openStream())); - } catch (MalformedURLException nourl) { - // if not a URL, maybe it is the XML itself - parser.parse (new StringReader (obj.toString())); - } else if (obj instanceof InputStream) { - parser.parse (new InputStreamReader ((InputStream) obj)); - } else if (obj instanceof Reader) { - parser.parse ((Reader) obj); - } - doc = parser.getDocument (); - return doc; + Document doc = null; + HtmlParser parser = new HtmlParser(); + + if (obj instanceof String) { + try { + // first try to interpret string as URL + URL url = new URL(obj.toString()); + + parser.parse(new InputStreamReader(url.openStream())); + } catch (MalformedURLException nourl) { + // if not a URL, maybe it is the XML itself + parser.parse(new StringReader(obj.toString())); + } + } else if (obj instanceof InputStream) { + parser.parse(new InputStreamReader((InputStream) obj)); + } else if (obj instanceof Reader) { + parser.parse((Reader) obj); + } + + doc = parser.getDocument(); + + return doc; } - - }