diff --git a/src/Acme/Serve/FileServlet.java b/src/Acme/Serve/FileServlet.java index 3a8c59a3..427a1fda 100644 --- a/src/Acme/Serve/FileServlet.java +++ b/src/Acme/Serve/FileServlet.java @@ -179,7 +179,7 @@ public class FileServlet extends HttpServlet protected void serveFile( HttpServletRequest req, HttpServletResponse res, boolean headOnly, String path, String filename, File file ) throws IOException { - // log( "getting " + path ); + log( "getting " + path ); if ( ! file.canRead() ) { res.sendError( HttpServletResponse.SC_FORBIDDEN ); diff --git a/src/Acme/Serve/Serve.java b/src/Acme/Serve/Serve.java index a7c8390b..49214072 100644 --- a/src/Acme/Serve/Serve.java +++ b/src/Acme/Serve/Serve.java @@ -147,8 +147,6 @@ public class Serve implements ServletContext, Runnable private int port; private PrintStream logStream; Acme.WildcardDictionary registry; - // the servlet to use if no other matches the request - Servlet defaultServlet; Properties props; /// Constructor. @@ -270,10 +268,6 @@ public class Serve implements ServletContext, Runnable registry.remove (urlPat); } - public void setDefaultServlet (Servlet servlet) { - defaultServlet = servlet; - } - /// Register a standard set of Servlets. These will return // files or directory listings, and run CGI programs, much like a // standard HTTP server. @@ -457,7 +451,8 @@ public class Serve implements ServletContext, Runnable // Same as the CGI variable SERVER_SOFTWARE. public String getServerInfo() { - return ServeUtils.serverName + " " + helma.main.Server.version + " (" + ServeUtils.serverUrl + ")"; + return ServeUtils.serverName + " " + ServeUtils.serverVersion + + " (" + ServeUtils.serverUrl + ")"; } /// Returns the value of the named attribute of the network service, or @@ -658,11 +653,6 @@ class ServeConnection implements Runnable, HttpServletRequest, HttpServletRespon if (reqQuery != null) reqQuery = decode (reqQuery); Servlet servlet = (Servlet) serve.registry.get( reqUriPath ); - // maybe the application name without slash? try with slash appended - if (servlet == null) - servlet = (Servlet) serve.registry.get (reqUriPath+"//"); - if (servlet == null) - servlet = serve.defaultServlet; if ( servlet != null ) runServlet( (HttpServlet) servlet ); else if ( "/".equals( reqUriPath )) @@ -687,7 +677,7 @@ class ServeConnection implements Runnable, HttpServletRequest, HttpServletRespon setStatus( SC_OK ); setDateHeader( "Date", System.currentTimeMillis() ); setHeader( - "Server", ServeUtils.serverName + "/" + helma.main.Server.version ); + "Server", ServeUtils.serverName + "/" + ServeUtils.serverVersion ); setHeader( "Connection", "close" ); try { diff --git a/src/Acme/Serve/ServeUtils.java b/src/Acme/Serve/ServeUtils.java index d1c73264..b580ae63 100644 --- a/src/Acme/Serve/ServeUtils.java +++ b/src/Acme/Serve/ServeUtils.java @@ -42,9 +42,8 @@ public class ServeUtils { // Server identification. - public static final String serverName = "Helma"; - // we're using the server version from helma.main.Server class. - // public static final String serverVersion = "1.2 p1"; + public static final String serverName = "Hop"; + public static final String serverVersion = "1.1 p1"; public static final String serverUrl = "http://helma.org/"; /// Write a standard-format HTML address for this server. @@ -53,7 +52,7 @@ public class ServeUtils PrintStream p = new PrintStream( o ); p.println( "
" + - serverName + " " + helma.main.Server.version + "
" ); + serverName + " " + serverVersion + "" ); } @@ -92,4 +91,3 @@ public class ServeUtils } } - diff --git a/src/FESI/Data/ESLoader.java b/src/FESI/Data/ESLoader.java index 7566809a..b6b05d58 100644 --- a/src/FESI/Data/ESLoader.java +++ b/src/FESI/Data/ESLoader.java @@ -260,11 +260,8 @@ public abstract class ESLoader extends ESObject { return (ESArrayWrapper) obj; // An array wrapper received externally } else if (obj.getClass().isArray()) { return new ESArrayWrapper(obj, evaluator); - } // else if (obj instanceof helma.framework.IPathElement) { // Hannes Wallnoefer, 13. Aug 2001 - // return evaluator.reval.getElementWrapper ((helma.framework.IPathElement) obj); - // } - // return new ESWrapper(obj, evaluator); - return evaluator.reval.getObjectWrapper (obj); + } + return new ESWrapper(obj, evaluator); } /** @@ -338,7 +335,6 @@ public abstract class ESLoader extends ESObject { * Check that each object in the paremeter array can be converted * to the type specified by the target array and convert them if needed. *

Even if the parameters are compatible with an EcmaScript value, - * some may have to be converted to an intermediate type. For example * an EcmaScript string of 1 character long is compatible with a Java * Character, but some conversion is needed. Arrays need a similar @@ -386,7 +382,6 @@ public abstract class ESLoader extends ESObject { if (targetClass.isPrimitive()) { // or: Object.class.isAssignableFrom(targetClass) accepted = false; debugInfo = " rejected (null cannot be assigned to primitive)"; - } else { accepted = true; debugInfo = " accepted (null to Object)"; diff --git a/src/FESI/Data/ESObject.java b/src/FESI/Data/ESObject.java index 17899d32..162f7d02 100644 --- a/src/FESI/Data/ESObject.java +++ b/src/FESI/Data/ESObject.java @@ -105,16 +105,6 @@ public abstract class ESObject extends ESValue { } - /** - * Allow the prototype to be set, added 2001-04-05 by Hannes Wallnöfer - * - * @param prototype The new prototype object - */ - public void setPrototype(ESObject prototype) { - this.prototype = prototype; - } - - /** * Return the name of the class of objects ([[class]]), as used in the default toString * method of objects (15.2.4.2) diff --git a/src/FESI/Data/ESWrapper.java b/src/FESI/Data/ESWrapper.java index 06e1172b..79413858 100644 --- a/src/FESI/Data/ESWrapper.java +++ b/src/FESI/Data/ESWrapper.java @@ -338,10 +338,9 @@ public class ESWrapper extends ESObject { return noPropertyMarker; } int modifiers = fld.getModifiers(); - // ALLOW ACCESS TO STATIC FIELDS. HW, 2001-11-27 - // if ((theObject == null) != Modifier.isStatic(modifiers)) { - // throw new EcmaScriptException("Field mode (static) not correct for "+ propertyName); - // } + if ((theObject == null) != Modifier.isStatic(modifiers)) { + throw new EcmaScriptException("Field mode (static) not correct for "+ propertyName); + } if (!Modifier.isPublic(modifiers)) { throw new EcmaScriptException("Field "+ propertyName + " not public"); } diff --git a/src/FESI/FESI.changes b/src/FESI/FESI.changes index f2cb4905..3332a08b 100644 --- a/src/FESI/FESI.changes +++ b/src/FESI/FESI.changes @@ -15,11 +15,3 @@ Changes to standard FESI distribution (1.1.4) for Hop ESNumber. - Made EcmaScriptFunctionVisitor recognise thisObj - Functions are now assigned to the current thisObject. -- Added setPrototype() method to FESI.Data.ESObject to be able to manipulate - the prototype after the object was created. -- Changed FESI.Interpreter.EcmaScriptEvaluateVisitor to use .equals instead of - == in equal method for helma.framework.core.ESNode objects, since different - wrappers may wrap the same object. -- Added RequestEvaluator reval field to FESI.Interpreter.Evaluator in order to - let ESLoader crate wrappers with correct prototype for path element objects. - diff --git a/src/FESI/Interpreter/EcmaScriptEvaluateVisitor.java b/src/FESI/Interpreter/EcmaScriptEvaluateVisitor.java index 4a0d7959..f9350cb7 100644 --- a/src/FESI/Interpreter/EcmaScriptEvaluateVisitor.java +++ b/src/FESI/Interpreter/EcmaScriptEvaluateVisitor.java @@ -248,13 +248,6 @@ public class EcmaScriptEvaluateVisitor return b1==b2; } - // ESNode wrappers must be checked with equals() because - // it's possible that different wrappers wrap the same node! - if (v1 instanceof helma.scripting.fesi.ESNode || - v1 instanceof helma.scripting.fesi.ESGenericObject) { - return v1.equals (v2); - } - return v1 == v2; } @@ -1363,5 +1356,3 @@ public class EcmaScriptEvaluateVisitor } } - - diff --git a/src/FESI/Interpreter/Evaluator.java b/src/FESI/Interpreter/Evaluator.java index 21f13952..3380641a 100644 --- a/src/FESI/Interpreter/Evaluator.java +++ b/src/FESI/Interpreter/Evaluator.java @@ -44,8 +44,6 @@ public class Evaluator { // used to stop thread, 06.12.99 Hannes Wallnoefer public volatile Thread thread; - // used to retrieve wrappers with correct Prototype for path elements in ESLoader - public helma.scripting.fesi.FesiEvaluator reval; private static String eol = System.getProperty("line.separator", "\n"); diff --git a/src/helma/doc/DocApplication.java b/src/helma/doc/DocApplication.java deleted file mode 100644 index 06d6b322..00000000 --- a/src/helma/doc/DocApplication.java +++ /dev/null @@ -1,119 +0,0 @@ -package helma.doc; - -import helma.framework.IPathElement; -import helma.main.Server; -import java.io.*; -import java.util.*; - -public class DocApplication extends DocElement implements IPathElement { - - private DocPrototype[] prototypes; - - /** read all prototypes - * @param name application to be documented - * @param appDir directory of this application */ - public DocApplication(String name, String appDir) throws DocException { - super( name, appDir, APPLICATION ); - checkCommentFile(); - readPrototypes(); - } - - public String getFullName() { - return ( "Application " + name ); - } - - /** return number of prototypes */ - public int countPrototypes() { - return prototypes.length; - } - - /** return a single prototype */ - public DocPrototype getDocPrototype(String name) { - for ( int i=0; ie2.getType() ) - return 1; - else if ( mode==BY_TYPE && e1.getType()=METHOD && type<=SKIN ) return true; else return false; } - public boolean isAction() { return (type==ACTION)?true:false; } - public boolean isTemplate() { return (type==TEMPLATE)?true:false; } - public boolean isFunction() { return (type==FUNCTION)?true:false; } - public boolean isMacro() { return (type==MACRO)?true:false; } - public boolean isSkin() { return (type==SKIN)?true:false; } - - public int getType() { return type; } - public String getTypeName() { return typeNames[type]; } - - public String getDocFileName() { return (typeNames[type] + "_" + name).toLowerCase() + ".html"; } - - /** @return the text of the comment */ - public String getComment() { return (comment!=null)?comment:""; } - - public int countTags() { return countTags(-1); } - public int countTags(int kind) { - int ct=0; - for ( int i=0; ibeginLine && ct"); - else - print(""); - print(""); - print(""); - println("" + title + ""); - if ( frameset==true ) - print(""); - println(""); - } - - - public void printFrameSet() { - println (""); - println (""); - println (""); - println ("sorry, your browser doesn't understand frames!"); - } - - /** print app title for left frame */ - public void printAppIndexTitle(String title) { - print("
"); - print(""); - print("Application " + title + "
" ); - } - - /** print prototype list for left frame */ - public void printAppIndexList(DocElement[] pt) { - print("
"); - print("

Prototypes

"); - for ( int i=0; i" + pt[i].getName() + "
"); - } - print ("

"); - } - - /** navigation on top of the page **/ - public void printNavBar(String name, DocPrototype pt, int page ) { - String urlPrefix = ( page==METHOD ) ? "../" : ""; - print(""); - print(""); - print(""); - print(""); - if ( pt!=null && page!=METHOD ) { - print(""); - } - - print("
Application " + name + "
"); - print("
"); - print(""); - } - - public void printElementTitle(DocElement docEl) { - print("


" + docEl.getName() + "
"); - print(docEl.getFullName() + "

"); - print("
"); - } - - public void printComment(DocElement docEl) { - if( docEl.getComment().length()>0 || docEl.countTags()>0 ) { - print("
" + docEl.getComment() + "
"); - print("
"); - for ( int i=0; i0 ) { - print("
" + DocTag.kindDesc[i] + "
"); - DocTag[] dt = docEl.listTags(i); - for ( int j=0; j" + renderTag(dt[j],j) + ""); - } - } - } - print("
"); - } - } - - private String renderFunctionName(DocFunction func) { - StringBuffer buf = new StringBuffer (); - buf.append(""); - if ( DocRun.getOption("-f").equals("true") ) - buf.append("" ); - if ( func.isMacro() ) { - buf.append( func.getDocPrototype().getName()+"."+func.getName().substring(0,func.getName().length()-6) ); - } else { - buf.append(func.getName().trim()); - if( func.isTemplate() || func.isFunction() ) { - buf.append("("); - int ct = func.countTags(DocTag.ARG); - for ( int i=0; i"); - if ( func.isFunction() || func.isMacro() ) - buf.append(" in " + (new File(func.getLocation())).getName() + "" ); - return buf.toString(); - } - - private String renderTag(DocTag tag) { - return renderTag(tag,0); - } - - private String renderTag(DocTag tag, int i) { - int kind = tag.getKind(); - String text = tag.getText(); - String name = tag.getName(); - switch (kind) { - case DocTag.ARG: - return( "Argument " + i + ": " + text ); - case DocTag.PARAM: - return( "Parameter " + name + " " + text ); - case DocTag.RETURNS: - case DocTag.AUTHOR: - case DocTag.VERSION: - case DocTag.RELEASE: - return( text ); - case DocTag.SEE: - if ( text.startsWith("http://") ) { - StringTokenizer tok = new StringTokenizer (text.trim()," "); - String url = (tok.countTokens()>1)?tok.nextToken():text; - return( "" + ((tok.countTokens()>0)?text.substring(url.length(),text.length()):text) + "" ); - } else { - StringBuffer buf = new StringBuffer(); - StringTokenizer tok = new StringTokenizer (text.trim(),"."); - if ( tok.countTokens()==0 ) return text; - DocPrototype dp = app.getDocPrototype( tok.nextToken() ); - if ( dp==null ) return text; - buf.append("0 ) - df = dp.getFunction( tok.nextToken() ); - if ( df==null ) - buf.append( link(dp) + "\">" + dp.getName() ); - else - buf.append( link(df) + "\">" + dp.getName() + "." + df.getName() ); - return(buf.toString()+""); - } - } - return text; - } - - public void printListHeader(String title) { - print(""); - print(""); - print(""); - } - - public void printPrototypeList(DocPrototype[] dl, String title) { - if ( dl.length==0 ) return; - printListHeader(title); - for ( int i=0; i"); - print(""); - } - print("
" + title + "
"); - print(""); - print(" " + dl[i].getTypeName() + "" + dl[i].getName() + ""); - print("
          " + dl[i].getComment() + "
" ); - print("

"); - } - - public void printFunctionList(DocFunction[] dl, String title) { - if ( dl.length==0 ) return; - printListHeader(title); - for ( int i=0; i"); - print(""); - print(""); - print(" " + dl[i].getTypeName() + ""); - print("
" + renderFunctionName(dl[i]) ); - print("
"); - printComment(dl[i]); - print("
"); - } - print("
"); - } - - public void printFunctionIndex(DocFunction[] dl) { - if ( dl.length==0 ) return; - String curChar = " "; - print("
"); - for ( int i=0; i

" + name.substring(0,1).toUpperCase() + "

"); - curChar = name.substring(0,1).toLowerCase(); - } - print("
" + dl[i].getName() + " - " + dl[i].getTypeName() + " in " + dl[i].getDocPrototype().getName() + "" ); - } - print("
"); - } - - public void printInheritance(DocPrototype pt) { - if ( pt.getName().equalsIgnoreCase("hopobject") ) - return; - DocApplication app = pt.getApplication(); - DocPrototype hopobject = (DocPrototype)app.getDocPrototype("hopobject"); - if ( hopobject==null || hopobject.countFunctions()==0 ) - return; - print(""); - print(""); - print(""); - print(""); - print("
Methods inherited from Prototype hopobject
"); - DocFunction[] df = hopobject.listFunctions(); - - int lastType = -1; - StringBuffer buf1 = new StringBuffer(); - StringBuffer buf2 = new StringBuffer(); - - for ( int i=0; i0 && buf2.length()>0 ) { - buf1.append("" + DocElement.typeNames[lastType] + ": " ); - buf1.append( buf2.toString().substring(0, buf2.toString().length()-2) ); - buf1.append("
"); - buf2 = new StringBuffer(); - } - lastType = df[i].getType(); - buf2.append ( "" + df[i].getName() + ", " ); - } - if ( buf2.length()>0 ) { - buf1.append("" + DocElement.typeNames[lastType] + ": " ); - buf1.append( buf2.toString().substring(0, buf2.toString().length()-2) ); - buf1.append("
"); - } - print ( buf1.toString() ); - print("
"); - } - - public void printFunction(DocFunction func) { - print( "

in " + func.getDocPrototype().getName() + "/" + (new File(func.getLocation())).getName() + ":" ); - print( "
");
-		print( HtmlEncoder.encodeAll(func.getSource()) );
-		print( "
" ); - } - - public void printStyleSheet() { - println( "/* Javadoc style sheet */"); - println( "/* Define colors, fonts and other style attributes here to override the defaults */"); - println( "* Page background color */"); - println( "body { background-color: #FFFFFF }"); - println( "/* Table colors */"); - println( "#TableHeadingColor { background: #CCCCFF } /* Dark mauve */"); - println( "#TableSubHeadingColor { background: #EEEEFF } /* Light mauve */"); - println( "#TableRowColor { background: #FFFFFF } /* White */"); - println( "/* Font used in left-hand frame lists */"); - println( "#FrameTitleFont { font-size: normal; font-family: normal }"); - println( "#FrameHeadingFont { font-size: normal; font-family: normal }"); - println( "#FrameItemFont { font-size: normal; font-family: normal }"); - println( "/* Example of smaller, sans-serif font in frames */"); - println( "/* #FrameItemFont { font-size: 10pt; font-family: Helvetica, Arial, sans-serif } */"); - println( "/* Navigation bar fonts and colors */"); - println( "#NavBarCell1 { background-color:#EEEEFF;}/* Light mauve */"); - println( "#NavBarCell1Rev { background-color:#00008B;}/* Dark Blue */"); - println( "#NavBarFont1 { font-family: Arial, Helvetica, sans-serif; color:#000000;}"); - println( "#NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}"); - println( "#NavBarCell2 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}"); - println( "#NavBarCell3 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}"); - } - - public String link(DocElement docEl) { - if ( docEl.isPrototype() ) { - return "prototype_" + docEl.getName() + ".html"; - } else if ( docEl.isMethod() ) { - DocFunction df = (DocFunction)docEl; - return "prototype_" + df.getDocPrototype().getName() + ".html#" + df.getName(); - } else { - return ""; - } - } - -} - diff --git a/src/helma/doc/DocPrototype.java b/src/helma/doc/DocPrototype.java deleted file mode 100644 index 7af3120d..00000000 --- a/src/helma/doc/DocPrototype.java +++ /dev/null @@ -1,221 +0,0 @@ -package helma.doc; - -import helma.framework.IPathElement; -import java.io.*; -import java.util.*; -import FESI.Parser.*; - - -public class DocPrototype extends DocElement implements IPathElement { - - DocFunction[] functions; - DocApplication app; - - public DocPrototype( String name, String location, DocApplication app ) throws DocException { - super( name, location, PROTOTYPE ); - this.app = app; - if ( name.equals("hopobject")==false && name.equals("global")==false ) - readPropertiesFile( new File(location,"type.properties").getAbsolutePath()); - checkCommentFile(); - readFunctions(); - } - - public String getFullName() { return ( "Prototype " + name ); } - - /** return number of functions */ - public int countFunctions() { return functions.length; } - - /** return array of functions of a special type */ - public int countFunctions(int type) { - int ct = 0; - for ( int i=0; i")) ); - EcmaScriptTokenManager mgr = new EcmaScriptTokenManager(new ASCII_CharStream(str,1,1),0); - Token tok = mgr.getNextToken(); - func = new DocFunction( fileToFuncName(f), f, DocElement.TEMPLATE, this, getCommentFromToken(tok) ); - } catch ( IndexOutOfBoundsException e ) { - func = new DocFunction( fileToFuncName(f), f, DocElement.TEMPLATE, this, "" ); - } - func.setSource(content); - funcVec.addElement(func); - } - - private void readSkin(String f, Vector funcVec) throws DocException { - // can't be commented yet - // simply add them - DocFunction func = new DocFunction(fileToFuncName(f),f,DocElement.SKIN, this,""); - func.readSource(f); - funcVec.addElement(func); - } - - - /** create token manager for a file */ - private EcmaScriptTokenManager createTokenManager(File f) { - try { - ASCII_CharStream is = new ASCII_CharStream(new FileReader(f), 1, 1); - EcmaScriptTokenManager mgr = new EcmaScriptTokenManager(is,0); - return mgr; - } catch ( FileNotFoundException shouldnotappear ) { } - return null; - } - - /** connect all available specialTokens */ - private String getCommentFromToken(Token tok) { - StringBuffer buf = new StringBuffer(); - while( tok.specialToken!=null ) { - buf.append(tok.specialToken.toString() ); - tok = tok.specialToken; - } - return ( buf.toString().trim() ); - } - - private String fileToFuncName(String f) { - return fileToFuncName(new File(f)); - } - - private String fileToFuncName(File f) { - String tmp = f.getName(); - return tmp.substring(0,tmp.indexOf(".")); - } - - public String toString() { - return ( "[DocPrototype " + name + "]" ); - } - - - //////////////////////////////////// - // from helma.framework.IPathElement - //////////////////////////////////// - - public String getElementName() { - return name; - } - - public IPathElement getChildElement(String name) { - for ( int i=0; i 0) { - if (buf.toString().length() == 0) { - usageError = true; - } else { - options.put(name, buf.toString()); - } - } - name = args[i]; - buf = new StringBuffer(); - } else { - buf.append(((buf.toString().length() > 0) ? " " : "") + args[i]); - } - } - options.put(name, buf.toString()); - // include last option - // now check parameter - if (options.containsKey("-h")) { - hopHomeDir = (String) options.get("-h"); - } else { - hopHomeDir = System.getProperty("user.dir"); - } - readHopProperties(hopHomeDir); - String parAppDir = ""; - if (options.containsKey("-a")) { - parAppDir = (String) options.get("-a"); - } else { - usageError = true; - } - if (usageError == true) { - help(); - System.exit(0); - } - try { - new DocRun(parAppDir); - } catch (DocException e) { - System.out.println("doc error: " + e.getMessage()); - } - } - - - /** - * Description of the Method - */ - public static void help() { - System.out.println("usage: java helma.doc.DocApplication -a appdir [-f] [-h hopdir] [-d docdir] [-i ignore]"); - System.out.println(" -a appdir Specify source directory"); - System.out.println(" -h hopdir Specify hop home directory"); - System.out.println(" -d docdir Specify destination directory"); - System.out.println(" -f true Link functions to source code"); - System.out.println(" -i ignore Specify prototypes to ignore (like: \"-i CVS mistsack\")"); - System.out.println(" -debug"); - System.out.println("\n"); - } - - - /** - * Description of the Method - * - *@param name Description of Parameter - *@return Description of the Returned Value - */ - public static boolean prototypeAllowed(String name) { - String ig = " " + getOption("-i").toLowerCase() + " "; - if (ig.equals("")) { - return true; - } - name = name.toLowerCase(); - if (ig.indexOf(" " + name + " ") > -1) { - return false; - } else { - return true; - } - } - - - /** - * reads server.properties, apps.properties and db.properties from - * hop-home-directory TBD: should be cleaned up to work exactly like the - * helma server - * - *@param hopHomeDir Description of Parameter - */ - public static void readHopProperties(String hopHomeDir) { - propfile = new File(hopHomeDir, "server.properties").getAbsolutePath(); - sysProps = new SystemProperties(propfile); - dbProps = new SystemProperties(new File(hopHomeDir, "db.properties").getAbsolutePath()); - actionExtension = sysProps.getProperty("actionExtension", ".hac"); - scriptExtension = sysProps.getProperty("scriptExtension", ".js"); - templateExtension = sysProps.getProperty("templateExtension", ".hsp"); - } - - - /** - * Description of the Method - * - *@param msg Description of Parameter - */ - public static void debug(String msg) { - if (options.containsKey("-debug")) { - System.out.println(msg); - } - } - - - /** - * Description of the Method - * - *@param msg Description of Parameter - */ - public static void log(String msg) { - System.out.println(msg); - } - -} - - diff --git a/src/helma/doc/DocTag.java b/src/helma/doc/DocTag.java deleted file mode 100644 index 63c5ee70..00000000 --- a/src/helma/doc/DocTag.java +++ /dev/null @@ -1,59 +0,0 @@ -package helma.doc; - -import java.util.*; - -public class DocTag { - - public static final int ARG = 0; - public static final int PARAM = 1; - public static final int RETURNS = 2; - public static final int AUTHOR = 3; - public static final int VERSION = 4; - public static final int RELEASE = 5; - public static final int SEE = 6; - - /** number of different tag-types **/ - public static final int TYPE_COUNT = 7; - - /** constants are used as array indices! **/ - public static final String[] kindNames = {"arg","param","return","author","version","release","see"}; - public static final String[] kindDesc = {"Arguments","Parameter","Returns","Author","Version","Release","See also"}; - - private String name; - private int kind; - private String text; - - public DocTag( String rawTag ) throws DocException { - //DocRun.log(rawTag); - try { - StringTokenizer tok = new StringTokenizer(rawTag); - String kindstr = tok.nextToken().toLowerCase(); - for ( int i=0; i - * - * Please note that this interface is still work in progress. You should expect it to get some - * additional methods that allow for looping through child elements, for example, or retrieving the - * parent element.

- * - */ - -public interface IPathElement { - - /** - * Return the name to be used to get this element from its parent - */ - public String getElementName (); - - /** - * Retrieve a child element of this object by name. - */ - public IPathElement getChildElement (String name); - - /** - * Return the parent element of this object. - */ - 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 (); - -} - - - - diff --git a/src/helma/framework/IRemoteApp.java b/src/helma/framework/IRemoteApp.java index 5bbe2b65..4970e536 100644 --- a/src/helma/framework/IRemoteApp.java +++ b/src/helma/framework/IRemoteApp.java @@ -4,7 +4,6 @@ package helma.framework; import java.rmi.*; -import java.util.Vector; /** * RMI interface for an application. Currently only execute is used and supported. @@ -14,6 +13,8 @@ public interface IRemoteApp extends Remote { public ResponseTrans execute (RequestTrans param) throws RemoteException; + public ResponseTrans get (String path, String sessionID) throws RemoteException; + public void ping () throws RemoteException; } diff --git a/src/helma/framework/IReplicatedApp.java b/src/helma/framework/IReplicatedApp.java deleted file mode 100644 index 8b1bd732..00000000 --- a/src/helma/framework/IReplicatedApp.java +++ /dev/null @@ -1,17 +0,0 @@ -// IReplicatedApp.java -// Copyright (c) Hannes Wallnöfer 2001 - -package helma.framework; - -import java.rmi.*; -import java.util.Vector; - -/** - * RMI interface for an application that is able to replicate it's node cache. - */ - -public interface IReplicatedApp extends Remote { - - public void replicateCache (Vector add, Vector delete) throws RemoteException; - -} diff --git a/src/helma/framework/RedirectException.java b/src/helma/framework/RedirectException.java index 1e43d979..21a54d00 100644 --- a/src/helma/framework/RedirectException.java +++ b/src/helma/framework/RedirectException.java @@ -3,13 +3,14 @@ package helma.framework; +import FESI.Exceptions.EcmaScriptException; -/** +/** * RedirectException is thrown internally when a response is redirected to a * new URL. */ - -public class RedirectException extends RuntimeException { + +public class RedirectException extends EcmaScriptException { String url; @@ -17,17 +18,17 @@ public class RedirectException extends RuntimeException { super ("Redirection Request to "+url); this.url = url; } - + public String getMessage () { - return url; + return url; } + + public void printStackTrace(java.io.PrintStream s) { - public void printStackTrace(java.io.PrintStream s) { - // do nothing } public void printStackTrace(java.io.PrintWriter w) { - // do nothing + } } diff --git a/src/helma/framework/RequestTrans.java b/src/helma/framework/RequestTrans.java index 16c8a212..b45fcc33 100644 --- a/src/helma/framework/RequestTrans.java +++ b/src/helma/framework/RequestTrans.java @@ -6,83 +6,52 @@ package helma.framework; import java.io.*; import java.util.*; import helma.objectmodel.*; -import helma.xmlrpc.Base64; /** * 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 { +public class RequestTrans implements Serializable { - // 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; + private Hashtable values; // this is used to hold the EcmaScript form data object public transient Object data; // when was execution started on this request? public transient long startTime; - // 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 (); + super (); + values = new Hashtable (); } - /** - * Create a new request transmitter with the given data map. - */ - public RequestTrans (byte method) { - httpMethod = method; - values = new HashMap (); + public RequestTrans (Hashtable values) { + this.values = values; } - /** - * Set a parameter value in this request transmitter. - */ public void set (String name, Object value) { values.put (name, value); } + public Enumeration keys () { + return values.keys (); + } - /** - * Get a value from the requests map by key. - */ public Object get (String name) { try { return values.get (name); } catch (Exception x) { - return null; + return null; } } - /** - * Get the data map for this request transmitter. - */ - public Map getRequestData () { + public Hashtable getReqData () { 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 (); } @@ -97,80 +66,10 @@ public class RequestTrans implements Externalizable { RequestTrans other = (RequestTrans) what; return (session.equals (other.session) && path.equalsIgnoreCase (other.path) && - values.equals (other.getRequestData ())); + values.equals (other.getReqData ())); } catch (Exception x) { return false; } } - /** - * Return true if this object represents a HTTP GET Request. - */ - public boolean isGet () { - return httpMethod == 0; - } - - /** - * Return true if this object represents a HTTP GET Request. - */ - 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 (); - } - - /** - * 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); - } - - public String getUsername() { - 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; - } - - 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/ResponseTrans.java b/src/helma/framework/ResponseTrans.java index b6867a48..71dede66 100644 --- a/src/helma/framework/ResponseTrans.java +++ b/src/helma/framework/ResponseTrans.java @@ -13,112 +13,47 @@ import helma.util.*; * class are directly exposed to JavaScript as global property res. */ -public class ResponseTrans implements Externalizable { +public class ResponseTrans implements Serializable { - /** - * Set the MIME content type of the response. - */ public String contentType = "text/html"; - - /** - * Set the charset of the response. - */ - public String charset; - - /** - * used to allow or disable client side caching - */ - public boolean cache = true; - - /** - * Used for HTTP response code, if 0 code 200 OK will be used. - */ - public int status = 0; - - /** - * Used for HTTP authentication - */ - public String realm; - - // name of the skin to be rendered after completion, if any - public transient String skin = null; - // the actual response private byte[] response = null; - // contains the redirect URL - private String redir = null; + public String redirect = null; // cookies - String cookieKeys[]; - String cookieValues[]; - int cookieDays[]; + public String cookieKeys[]; + public String cookieValues[]; + public int cookieDays[]; int nCookies = 0; + // used to allow or disable client side caching + public boolean cache = true; + // 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 resolve skin names - private transient Object skinpath = null; - // the processed skinpath as array of Nodes or directory names - private transient Object[] translatedSkinpath = null; - - static final long serialVersionUID = -8627370766119740844L; - - /** - * the buffers used to build the single body parts - - * transient, response must be constructed before this is serialized - */ + // the buffers used to build the single body parts - + // transient, response must be constructed before this is serialized public transient String title, head, body, message, error; - /** - * JavaScript object to make the values Map accessible to - * script code as res.data - */ - public transient Object data; - - // the map of form and cookie data - private transient Map values; + // name of the skin to be rendered after completion, if any + public transient String mainSkin = null; public ResponseTrans () { super (); - title = head = body = message = error = null; - values = new HashMap (); + title = head = body = message = error = ""; } - - - /** - * Get a value from the responses map by key. - */ - 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; - } - - /** - * Reset the response object to its initial empty state. - */ + public void reset () { if (buffer != null) buffer.setLength (0); - response = null; - redir = null; - skin = null; - title = head = body = message = error = null; - values.clear (); + redirect = null; + mainSkin = null; + title = head = body = message = error = ""; } @@ -154,26 +89,6 @@ public class ResponseTrans implements Externalizable { } } - /** - * Utility function that appends a
to whatever is written - */ - public void writeln (Object what) { - if (buffer == null) - buffer = new StringBuffer (512); - 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 (512); - buffer.append (c, start, length); - } - /** * Replace special characters with entities, including <, > and ", thus allowing * no HTML tags. @@ -212,18 +127,6 @@ public class ResponseTrans implements Externalizable { } - /** - * Encode HTML entities, but leave newlines alone. This is for the content of textarea forms. - */ - public void encodeForm (Object what) { - if (what != null) { - if (buffer == null) - buffer = new StringBuffer (512); - HtmlEncoder.encodeAll (what.toString (), buffer, false); - } - } - - public void append (String what) { if (what != null) { if (buffer == null) @@ -233,51 +136,23 @@ public class ResponseTrans implements Externalizable { } public void redirect (String url) throws RedirectException { - redir = url; + redirect = url; throw new RedirectException (url); } - public String getRedirect () { - return redir; - } - - /** - * Allow to directly set the byte array for the response. Calling this more than once will - * 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; - } - /** * 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; - - boolean error = false; - if (response == null) { - if (buffer != null) { - try { - response = buffer.toString ().getBytes (charset); - } catch (UnsupportedEncodingException uee) { - error = true; - response = buffer.toString ().getBytes (); - } - buffer = null; // make sure this is done only once, even with more requsts attached - } else { - response = new byte[0]; - } + public synchronized void close () { + if (buffer != null) { + response = buffer.toString ().getBytes (); + buffer = null; // make sure this is done only once, even with more requsts attached + } else { + response = new byte[0]; } notifyAll (); - // if there was a problem with the encoding, let the app know - if (error) - throw new UnsupportedEncodingException (charset); } /** @@ -302,26 +177,7 @@ public class ResponseTrans implements Externalizable { } public String getContentType () { - if (charset != null) - return contentType+"; charset="+charset; - return contentType; - } - - public void setSkinpath (Object obj) { - this.skinpath = obj; - this.translatedSkinpath = null; - } - - public Object getSkinpath () { - return skinpath; - } - - public void setTranslatedSkinpath (Object[] arr) { - this.translatedSkinpath = arr; - } - - public Object[] getTranslatedSkinpath () { - return translatedSkinpath; + return contentType; } public synchronized void setCookie (String key, String value) { @@ -371,31 +227,6 @@ public class ResponseTrans implements Externalizable { return cookieValues[i]; } - public void readExternal (ObjectInput s) throws ClassNotFoundException, IOException { - contentType = (String) s.readObject (); - response = (byte[]) s.readObject (); - redir = (String) s.readObject (); - cookieKeys = (String[]) s.readObject (); - cookieValues = (String[]) s.readObject (); - cookieDays = (int[]) s.readObject (); - nCookies = s.readInt (); - cache = s.readBoolean (); - status = s.readInt (); - realm = (String) s.readObject (); - } - - public void writeExternal (ObjectOutput s) throws IOException { - s.writeObject (contentType); - s.writeObject (response); - s.writeObject (redir); - s.writeObject (cookieKeys); - s.writeObject (cookieValues); - s.writeObject (cookieDays); - s.writeInt (nCookies); - s.writeBoolean (cache); - s.writeInt (status); - s.writeObject (realm); - } } diff --git a/src/helma/scripting/fesi/FesiActionAdapter.java b/src/helma/framework/core/Action.java similarity index 63% rename from src/helma/scripting/fesi/FesiActionAdapter.java rename to src/helma/framework/core/Action.java index fb3d8287..0b35e313 100644 --- a/src/helma/scripting/fesi/FesiActionAdapter.java +++ b/src/helma/framework/core/Action.java @@ -1,15 +1,12 @@ -// ActionFile.java +// Action.java // Copyright (c) Hannes Wallnöfer 1998-2000 -package helma.scripting.fesi; +package helma.framework.core; -import helma.scripting.*; -import java.util.Vector; -import java.util.Iterator; +import java.util.*; import java.io.*; import helma.framework.*; -import helma.framework.core.*; -import helma.util.Updatable; +import helma.objectmodel.IServer; import FESI.Data.*; import FESI.Parser.*; import FESI.AST.ASTFormalParameterList; @@ -21,64 +18,90 @@ import FESI.Exceptions.*; /** - * An class that updates fesi interpreters with actionfiles and templates. + * An Action is a JavaScript function that is exposed as a URI. It is + * usually represented by a file with extension .hac (hop action file) + * that contains the pure JavaScript body of the function. */ -public class FesiActionAdapter { +public class Action { + String name; + String functionName; Prototype prototype; Application app; - String sourceName; + File file; + long lastmod; + // this is the parsed function which can be easily applied to RequestEvaluator objects TypeUpdater pfunc; - public FesiActionAdapter (ActionFile action) { - prototype = action.getPrototype (); - app = action.getApplication (); - String content = action.getContent (); - String functionName = action.getFunctionName (); - sourceName = action.toString (); - try { - pfunc = parseFunction (functionName, "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", content); - } catch (Throwable x) { - String message = x.getMessage (); - pfunc = new ErrorFeedback (functionName, message); - } + + public Action (File file, String name, Prototype proto) { + this.prototype = proto; + this.app = proto.app; + this.name = name; + this.file = file; + update (file); } - /* protected void update (FesiEvaluator fesi) throws Exception { - // app.logEvent ("Reading text template " + name); - FesiScriptingEnvironment scriptEnv = (FesiScriptingEnvironment) app.getScriptingEnvironment (); - Iterator evals = scriptEnv.getEvaluators().iterator(); - while (evals.hasNext ()) { - try { - FesiEvaluator fesi = (FesiEvaluator) evals.next (); - updateEvaluator (fesi); - } catch (Exception ignore) {} + public void update (File f) { + + this.file = f; + long fmod = file.lastModified (); + if (lastmod == fmod) + return; + + try { + FileReader reader = new FileReader (file); + char cbuf[] = new char[(int) file.length ()]; + reader.read (cbuf); + reader.close (); + String content = new String (cbuf); + update (content); + } catch (Exception filex) { + IServer.getLogger().log ("*** Error reading action file "+file+": "+filex); } - } */ + lastmod = fmod; + } + + + + public void update (String content) throws Exception { + // IServer.getLogger().log ("Reading text template " + name); + + functionName = name+"_hop_action"; + + try { + pfunc = parseFunction (functionName, "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", content); + } catch (Exception x) { + String message = x.getMessage (); + pfunc = new ErrorFeedback (functionName, message); + } - /* protected void remove () { Iterator evals = app.typemgr.getRegisteredRequestEvaluators (); while (evals.hasNext ()) { try { RequestEvaluator reval = (RequestEvaluator) evals.next (); - ObjectPrototype op = reval.getPrototype (prototype.getName()); - functionName = name+"_action"; - ESValue esv = (ESValue) op.getProperty (functionName, functionName.hashCode()); - if (esv instanceof ConstructedFunctionObject || esv instanceof ThrowException) { - op.deleteProperty (functionName, functionName.hashCode()); - } + updateRequestEvaluator (reval); } catch (Exception ignore) {} } - } */ + } - public synchronized void updateEvaluator (FesiEvaluator fesi) throws EcmaScriptException { + public String getName () { + return name; + } + + public String getFunctionName () { + return functionName; + } + + + public synchronized void updateRequestEvaluator (RequestEvaluator reval) throws EcmaScriptException { if (pfunc != null) - pfunc.updateEvaluator (fesi); + pfunc.updateRequestEvaluator (reval); } protected TypeUpdater parseFunction (String funcname, String params, String body) throws EcmaScriptException { @@ -122,10 +145,10 @@ public class FesiActionAdapter { sl = (ASTStatementList) parser.StatementList(); is.close(); } catch (ParseException x) { - app.logEvent ("Error parsing file "+app.getName()+":"+sourceName+": "+x); + IServer.getLogger().log ("Error parsing file "+app.getName()+":"+prototype.getName()+"/"+file.getName()+": "+x); throw new EcmaScriptParseException (x, new StringEvaluationSource(fulltext, null)); } catch (Exception x) { - app.logEvent ("Error parsing file "+app.getName()+":"+sourceName+": "+x); + IServer.getLogger().log ("Error parsing file "+app.getName()+":"+prototype.getName()+"/"+file.getName()+": "+x); throw new RuntimeException (x.getMessage ()); } @@ -150,15 +173,15 @@ public class FesiActionAdapter { this.functionName = functionName; } - public void updateEvaluator (FesiEvaluator fesi) throws EcmaScriptException { + public void updateRequestEvaluator (RequestEvaluator reval) throws EcmaScriptException { - ObjectPrototype op = fesi.getPrototype (prototype.getName()); + ObjectPrototype op = reval.getPrototype (prototype.getName()); - EcmaScriptVariableVisitor vdvisitor = fesi.evaluator.getVarDeclarationVisitor(); + EcmaScriptVariableVisitor vdvisitor = reval.evaluator.getVarDeclarationVisitor(); Vector vnames = vdvisitor.processVariableDeclarations(sl, fes); FunctionPrototype fp = ConstructedFunctionObject.makeNewConstructedFunction ( - fesi.evaluator, functionName, fes, + reval.evaluator, functionName, fes, fullFunctionText, fpl.getArguments(), vnames, sl); op.putHiddenProperty (functionName, fp); } @@ -174,12 +197,12 @@ public class FesiActionAdapter { errorMessage = msg; } - public void updateEvaluator (FesiEvaluator fesi) throws EcmaScriptException { + public void updateRequestEvaluator (RequestEvaluator reval) throws EcmaScriptException { - ObjectPrototype op = fesi.getPrototype (prototype.getName ()); + ObjectPrototype op = reval.getPrototype (prototype.getName ()); - FunctionPrototype fp = (FunctionPrototype) fesi.evaluator.getFunctionPrototype (); - FunctionPrototype func = new ThrowException (functionName, fesi.evaluator, fp, errorMessage); + FunctionPrototype fp = (FunctionPrototype) reval.evaluator.getFunctionPrototype (); + FunctionPrototype func = new ThrowException (functionName, reval.evaluator, fp, errorMessage); op.putHiddenProperty (functionName, func); } @@ -200,8 +223,38 @@ public class FesiActionAdapter { } interface TypeUpdater { - public void updateEvaluator (FesiEvaluator fesi) throws EcmaScriptException; + public void updateRequestEvaluator (RequestEvaluator reval) throws EcmaScriptException; } } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java index a85a7cb0..d0939a74 100644 --- a/src/helma/framework/core/Application.java +++ b/src/helma/framework/core/Application.java @@ -3,76 +3,52 @@ package helma.framework.core; -import helma.doc.DocApplication; -import helma.doc.DocException; -import helma.framework.*; -import helma.main.Server; -import helma.scripting.*; -import helma.scripting.fesi.ESUser; -import helma.objectmodel.*; -import helma.objectmodel.db.*; -import helma.xmlrpc.*; -import helma.util.*; -import com.sleepycat.db.DbException; import java.util.*; import java.io.*; -import java.net.URLEncoder; import java.lang.reflect.*; import java.rmi.*; import java.rmi.server.*; +import helma.framework.*; +import helma.objectmodel.*; +import helma.objectmodel.db.NodeManager; +import helma.objectmodel.db.WrappedNodeManager; +import helma.xmlrpc.*; +import helma.util.CacheMap; +import FESI.Data.*; +import FESI.Interpreter.*; +import com.sleepycat.db.DbException; + /** - * The central class of a Helma application. This class keeps a pool of so-called - * request evaluators (threads with JavaScript interpreters), waits for - * requests from the Web server or XML-RPC port and dispatches them to + * The central class of a HOP application. This class keeps a pool of so-called + * request evaluators (threads with JavaScript interpreters), waits for + * requests from the Web server or XML-RPC port and dispatches them to * the evaluators. */ -public class Application extends UnicastRemoteObject implements IRemoteApp, IPathElement, IReplicatedApp, Runnable { +public class Application extends UnicastRemoteObject implements IRemoteApp, Runnable { + SystemProperties props; + File appDir, dbDir; private String name; - SystemProperties props, dbProps; - File home, appDir, dbDir; protected NodeManager nmgr; + protected static WebServer xmlrpc; + protected XmlRpcAccess xmlrpcAccess; - // the class name of the scripting environment implementation - ScriptingEnvironment scriptingEngine; + private String baseURI; - // the root of the website, if a custom root object is defined. - // otherwise this is managed by the NodeManager and not cached here. - Object rootObject = null; - String rootObjectClass; + public boolean debug; - /** - * The type manager checks if anything in the application's prototype definitions - * has been updated prior to each evaluation. - */ - public TypeManager typemgr; + TypeManager typemgr; - /** - * The skin manager for this application - */ - protected SkinManager skinmgr; - - /** - * Each application has one internal request evaluator for calling - * the scheduler and other internal functions. - */ RequestEvaluator eval; - - /** - * Collections for evaluator thread pooling - */ protected Stack freeThreads; protected Vector allThreads; boolean stopped = false; - boolean debug; - public long starttime; - public Hashtable sessions; - public Hashtable activeUsers; + Hashtable sessions; + Hashtable activeUsers; Hashtable dbMappings; - Hashtable dbSources; Thread worker; long requestTimeout = 60000; // 60 seconds for request timeout. @@ -80,10 +56,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat // Map of requesttrans -> active requestevaluators Hashtable activeRequests; - - // Two logs for each application: events and accesses - Logger eventLog, accessLog; - + protected String templateExtension, scriptExtension, actionExtension, skinExtension; // A transient node that is shared among all evaluators @@ -92,123 +65,34 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat protected volatile long xmlrpcCount = 0; protected volatile long errorCount = 0; - protected static WebServer xmlrpc; - protected XmlRpcAccess xmlrpcAccess; - private String xmlrpcHandlerName; - - // the URL-prefix to use for links into this application - private String baseURI; - private DbMapping rootMapping, userRootMapping, userMapping; - // boolean checkSubnodes; + protected CacheMap skincache = new CacheMap (100); - // name of respone encoding - String charset; - // password file to use for authenticate() function - private CryptFile pwfile; - - // Map of java class names to object prototypes - Properties classMapping; - // Map of extensions allowed for public skins - Properties skinExtensions; - - // a cache for parsed skin objects - public CacheMap skincache = new CacheMap (200, 0.80f); - - // DocApplication used for introspection - public DocApplication docApp; - - /** - * Zero argument constructor needed for RMI - */ public Application () throws RemoteException { super (); } - /** - * Build an application with the given name in the home directory. Server-wide properties will - * be created if the files are present, but they don't have to. - */ - public Application (String name, File home) throws RemoteException, IllegalArgumentException { - this (name, home, - new SystemProperties (new File (home, "server.properties").getAbsolutePath ()), - new SystemProperties (new File (home, "db.properties").getAbsolutePath ())); - } - - /** - * Build an application with the given name, app and db properties and app base directory. The - * app directories will be created if they don't exist already. - */ - public Application (String name, File home, SystemProperties sysProps, SystemProperties sysDbProps) - throws RemoteException, IllegalArgumentException { - - if (name == null || name.trim().length() == 0) - throw new IllegalArgumentException ("Invalid application name: "+name); + public Application (String name, File dbHome, File appHome) throws RemoteException, DbException { this.name = name; - this.home = home; - // give the Helma Thread group a name so the threads can be recognized threadgroup = new ThreadGroup ("TX-"+name); - // check the system props to see if custom app directory is set. - // otherwise use /apps/ - String appHome = null; - if (sysProps != null) - appHome = sysProps.getProperty ("appHome"); - if (appHome != null && !"".equals (appHome.trim())) - appDir = new File (appHome); - else - appDir = new File (home, "apps"); - appDir = new File (appDir, name); + appDir = new File (appHome, name); if (!appDir.exists()) appDir.mkdirs (); - - // check the system props to see if custom embedded db directory is set. - // otherwise use /db/ - String dbHome = null; - if (sysProps != null) - dbHome = sysProps.getProperty ("dbHome"); - if (dbHome != null && !"".equals (dbHome.trim())) - dbDir = new File (dbHome); - else - dbDir = new File (home, "db"); - dbDir = new File (dbDir, name); + dbDir = new File (dbHome, name); if (!dbDir.exists()) dbDir.mkdirs (); - // create app-level properties File propfile = new File (appDir, "app.properties"); - props = new SystemProperties (propfile.getAbsolutePath (), sysProps); + props = new SystemProperties (propfile.getAbsolutePath (), IServer.sysProps); - // create app-level db sources - File dbpropfile = new File (appDir, "db.properties"); - dbProps = new SystemProperties (dbpropfile.getAbsolutePath (), sysDbProps); - - // the passwd file, to be used with the authenticate() function - File pwf = new File (home, "passwd"); - CryptFile parentpwfile = new CryptFile (pwf, null); - pwf = new File (appDir, "passwd"); - pwfile = new CryptFile (pwf, parentpwfile); - - // the properties that map java class names to prototype names - classMapping = new SystemProperties (new File (appDir, "class.properties").getAbsolutePath ()); - - // get class name of root object if defined. Otherwise native Helma objectmodel will be used. - rootObjectClass = classMapping.getProperty ("root"); - - // the properties that map allowed public skin extensions to content types - skinExtensions = new SystemProperties (new File (appDir, "mime.properties").getAbsolutePath ()); - - // character encoding to be used for responses - charset = props.getProperty ("charset", "ISO-8859-1"); + nmgr = new NodeManager (this, dbDir.getAbsolutePath (), props); debug = "true".equalsIgnoreCase (props.getProperty ("debug")); - // checkSubnodes = !"false".equalsIgnoreCase (props.getProperty ("subnodeChecking")); - - try { requestTimeout = Long.parseLong (props.getProperty ("requestTimeout", "60"))*1000l; } catch (Exception ignore) { } @@ -221,22 +105,16 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat sessions = new Hashtable (); activeUsers = new Hashtable (); dbMappings = new Hashtable (); - dbSources = new Hashtable (); - appnode = new TransientNode ("app"); - xmlrpc = helma.main.Server.getXmlRpcServer (); + appnode = new Node ("app"); + xmlrpc = IServer.getXmlRpcServer (); xmlrpcAccess = new XmlRpcAccess (this); } - /** - * Get the application ready to run, initializing the evaluators and type manager. - */ - public void init () throws DbException, ScriptingException { - scriptingEngine = new helma.scripting.fesi.FesiScriptingEnvironment (); - scriptingEngine.init (this, props); + public void start () { eval = new RequestEvaluator (this); - logEvent ("Starting evaluators for "+name); + IServer.getLogger().log ("Starting evaluators for "+name); int maxThreads = 12; try { maxThreads = Integer.parseInt (props.getProperty ("maxThreads")); @@ -252,10 +130,8 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat activeRequests = new Hashtable (); typemgr = new TypeManager (this); - typemgr.createPrototypes (); - // logEvent ("Started type manager for "+name); - - skinmgr = new SkinManager (this); + typemgr.check (); + IServer.getLogger().log ("Started type manager for "+name); rootMapping = getDbMapping ("root"); userMapping = getDbMapping ("user"); @@ -267,29 +143,17 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat userRootMapping = new DbMapping (this, "__userroot__", p); rewireDbMappings (); - nmgr = new NodeManager (this, dbDir.getAbsolutePath (), props); - - xmlrpcHandlerName = props.getProperty ("xmlrpcHandlerName", this.name); - if (xmlrpc != null) - xmlrpc.addHandler (xmlrpcHandlerName, new XmlRpcInvoker (this)); - - // typemgr.start (); - } - - /** - * Create request evaluators and start scheduler and cleanup thread - */ - public void start () { - starttime = System.currentTimeMillis(); worker = new Thread (this, "Worker-"+name); worker.setPriority (Thread.NORM_PRIORITY+2); worker.start (); - // logEvent ("session cleanup and scheduler thread started"); + IServer.getLogger().log ("session cleanup and scheduler thread started"); + + xmlrpc.addHandler (this.name, new XmlRpcInvoker (this)); + + typemgr.start (); } - /** - * This is called to shut down a running application. - */ + public void stop () { stopped = true; @@ -298,42 +162,25 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat if (worker != null) worker.interrupt (); worker = null; + typemgr.stop (); - if (xmlrpc != null && xmlrpcHandlerName != null) - xmlrpc.removeHandler (xmlrpcHandlerName); + xmlrpc.removeHandler (this.name); - // stop evaluators if (allThreads != null) { for (Enumeration e=allThreads.elements (); e.hasMoreElements (); ) { RequestEvaluator ev = (RequestEvaluator) e.nextElement (); ev.stopThread (); } } - - // remove evaluators allThreads.removeAllElements (); freeThreads = null; - - // shut down node manager and embedded db try { nmgr.shutdown (); } catch (DbException dbx) { System.err.println ("Error shutting down embedded db: "+dbx); } - - // stop logs if they exist - if (eventLog != null) { - eventLog.close (); - } - if (accessLog != null) { - accessLog.close (); - } - } - /** - * Returns a free evaluator to handle a request. - */ protected RequestEvaluator getEvaluator () { if (stopped) throw new ApplicationStoppedException (); @@ -344,19 +191,12 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat } } - /** - * Returns an evaluator back to the pool when the work is done. - */ protected void releaseEvaluator (RequestEvaluator ev) { if (ev != null) freeThreads.push (ev); } - /** - * This can be used to set the maximum number of evaluators which will be allocated. - * If evaluators are required beyound this number, an error will be thrown. - */ - public boolean setNumberOfEvaluators (int n) { + protected boolean setNumberOfEvaluators (int n) { if (n < 2 || n > 511) return false; int current = allThreads.size(); @@ -374,7 +214,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat try { RequestEvaluator re = (RequestEvaluator) freeThreads.pop (); allThreads.removeElement (re); - // typemgr.unregisterRequestEvaluator (re); + typemgr.unregisterRequestEvaluator (re); re.stopThread (); } catch (EmptyStackException empty) { return false; @@ -385,21 +225,10 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat return true; } - /** - * Return the number of currently active threads - */ - public int getActiveThreads () { - return 0; - } - - /** - * Execute a request coming in from a web client. - */ public ResponseTrans execute (RequestTrans req) { requestCount += 1; - - // get user for this request's session + User u = getUser (req.session); ResponseTrans res = null; @@ -432,114 +261,46 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat 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()); - } + res.close (); // this needs to be done before sending it back } else { res.waitForClose (); } } - + return res; } - /** - * 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 (props.getProperty ("allowReplication"))) - return; - nmgr.replicateCache (add, delete); + // get raw content from the database, circumventing the scripting framework. + // currently not used/supported. + public ResponseTrans get (String path, String sessionID) { + ResponseTrans res = null; + return res; } public void ping () { // do nothing } - /** - * Reset the application's object cache, causing all objects to be refetched from - * the database. - */ - public void clearCache () { - nmgr.clearCache (); - } - - /** - * Set the application's root element to an arbitrary object. After this is called - * 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; - } - - /** - * This method returns the root object of this application's object tree. - */ - public Object getDataRoot () { - // if rootObject is set, immediately return it. - if (rootObject != null) - return rootObject; - // check if we ought to create a rootObject from its class name - if (rootObjectClass != null) { - // create custom root element. - if (rootObject == null) { - try { - if ( classMapping.containsKey("root.factory.class") && classMapping.containsKey("root.factory.method") ) { - Class c = Class.forName( classMapping.getProperty("root.factory.class") ); - Method m = c.getMethod( classMapping.getProperty("root.factory.method"), null ); - rootObject = m.invoke(c, null); - } else { - Class c = Class.forName( 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 + public INode getDataRoot () { INode root = nmgr.safe.getNode ("0", rootMapping); root.setDbMapping (rootMapping); return root; } - /** - * 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; } - /** - * Returns a wrapper containing the node manager for this application. The node manager is - * 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; } - /** - * Return a transient node that is shared by all evaluators of this application ("app node") - */ - public INode getAppNode () { - return appnode; - } - - - /** - * Returns a Node representing a registered user of this application by his or her user name. - */ public INode getUserNode (String uid) { + if ("prototype".equalsIgnoreCase (uid)) + return null; try { INode users = getUserRoot (); return users.getNode (uid, false); @@ -553,56 +314,35 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat * 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); + public Prototype getPrototype (INode n) { + String protoname = n.getPrototype (); if (protoname == null) return typemgr.getPrototype ("hopobject"); - Prototype p = typemgr.getPrototype (protoname); - if (p == null) - p = typemgr.getPrototype ("hopobject"); - return p; + return typemgr.getPrototype (protoname); } - /** - * Return a collection containing all prototypes defined for this application - */ - public Collection getPrototypes () { - return typemgr.prototypes.values (); - } /** - * Retrurn 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) { - return skinmgr.getSkin (object, skinname, skinpath); - } - - /** - * Return the user currently associated with a given Hop session ID. This may be - * a registered or an anonymous user. + * Return the user currently associated with a given Hop session ID. */ public User getUser (String sessionID) { - if (sessionID == null) + if (sessionID == null) return null; - User u = (User) sessions.get (sessionID); - if (u != null) { - u.touch (); + User u = (User) sessions.get (sessionID); + if (u != null) { + u.touch (); } else { u = new User (sessionID, this); sessions.put (sessionID, u); } - return u; + return u; } - /** - * Register a user with the given user name and password. - */ public INode registerUser (String uname, String password) { - // Register a user who already has a user object - // (i.e. who has been surfing around) + // Register a user who already has a user object + // (i.e. who has been surfing around) if (uname == null) return null; uname = uname.toLowerCase ().trim (); @@ -616,33 +356,22 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat if (unode != null) return null; - unode = users.createNode (uname); + unode = new Node (uname); + unode.setString ("name", uname); + unode.setString ("password", password); 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); - // users.setNode (uname, unode); - // return users.getNode (uname, false); - return unode; + users.setNode (uname, unode); + return users.getNode (uname, false); } catch (Exception x) { - logEvent ("Error registering User: "+x); + IServer.getLogger().log ("Error registering User: "+x); return null; } } - /** - * Log in a user given his or her user name and password. - */ public boolean loginUser (String uname, String password, ESUser u) { - // Check the name/password of a user who already has a user object - // (i.e. who has been surfing around) + // Check the name/password of a user who already has a user object + // (i.e. who has been surfing around) if (uname == null) return false; uname = uname.toLowerCase ().trim (); @@ -654,21 +383,19 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat INode unode = users.getNode (uname, false); String pw = unode.getString ("password", false); if (pw != null && pw.equals (password)) { - // give the user his/her persistant node + // give the user her piece of persistence u.setNode (unode); - activeUsers.put (unode.getName (), u.user); + u.user.setNode (unode); + activeUsers.put (unode.getNameOrID (), u.user); return true; } - + } catch (Exception x) { return false; } return false; } - /** - * Log out a user from this application. - */ public boolean logoutUser (ESUser u) { if (u.user != null) { String uid = u.user.uid; @@ -676,86 +403,32 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat activeUsers.remove (uid); // switch back to the non-persistent user node as cache - u.setNode (null); + u.user.setNode (null); + u.setNode (u.user.getNode ()); } return true; } - /** - * In contrast to login, this works outside the Hop user object framework. Instead, the user is - * 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); - } - - /** - * Return the href to the root of this application. - */ - 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) { - // FIXME: will fail for non-node roots - Object root = getDataRoot (); + public String getNodePath (INode n, String tmpname) { + INode root = getDataRoot (); INode users = getUserRoot (); - - // check base uri and optional root prototype from app.properties - String base = props.getProperty ("baseURI"); - String rootproto = props.getProperty ("rootPrototype"); - - if (base != null || baseURI == null) - setBaseURI (base); - - // String href = n.getUrl (root, users, tmpname, siteroot); - - String divider = "/"; - StringBuffer b = new StringBuffer (); - Object p = elem; - int loopWatch = 0; - - while (p != null && getParentElement (p) != null && p != root) { - - if (rootproto != null && rootproto.equals (getPrototypeName (p))) - break; - b.insert (0, divider); - - // users always have a canonical URL like /users/username - if ("user".equals (getPrototypeName (p))) { - b.insert (0, URLEncoder.encode (getElementName (p))); - p = users; - break; - } - b.insert (0, URLEncoder.encode (getElementName (p))); - p = getParentElement (p); - - if (loopWatch++ > 20) - break; - } - - if (p == users) { - b.insert (0, divider); - b.insert (0, "users"); - } - - if (actionName != null) - b.append (URLEncoder.encode (actionName)); - - return baseURI + b.toString (); + String href = n.getUrl (root, users, tmpname); + return href; } - - /** - * This method sets the base URL of this application which will be prepended to - * the actual object path. - */ + public String getNodeHref (INode n, String tmpname) { + INode root = getDataRoot (); + INode users = getUserRoot (); + // check base uri from app.properties + String base = props.getProperty ("baseURI"); + if (base != null) + setBaseURI (base); + String href = n.getHref (root, users, tmpname, baseURI == null ? "/" : baseURI); + // add cache teaser + // href = href + "&tease="+((int) (Math.random ()*999)); + return href; + } + public void setBaseURI (String uri) { if (uri == null) this.baseURI = "/"; @@ -765,193 +438,15 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat this.baseURI = uri; } - /** - * Tell other classes whether they should output logging information for this application. - */ - public boolean debug () { - return debug; - } - - private Object invokeFunction (Object obj, String func, Object[] args) { - Thread thread = Thread.currentThread (); - RequestEvaluator reval = null; - int l = allThreads.size(); - for (int i=0; i cleanupSleep) try { - lastCleanup = now; - // logEvent ("Cleaning up "+name+": " + sessions.size () + " sessions active"); + try { + worker.sleep (cleanupSleep); + } catch (InterruptedException x) { + IServer.getLogger().log ("Scheduler for "+name+" interrupted"); + worker = null; + break; + } + try { + IServer.getLogger().log ("Cleaning up "+name+": " + sessions.size () + " sessions active"); + long now = System.currentTimeMillis (); Hashtable cloned = (Hashtable) sessions.clone (); for (Enumeration e = cloned.elements (); e.hasMoreElements (); ) { User u = (User) e.nextElement (); - if (now - u.lastTouched () > sessionTimeout * 60000) { + if (now - u.touched () > sessionTimeout * 60000) { if (u.uid != null) { try { - eval.invokeFunction (u, "onLogout", new Object[0]); - } catch (Exception ignore) {} + eval.invokeFunction (u, "onLogout", new ESValue[0]); + } catch (Exception ignore) { + ignore.printStackTrace (); + } activeUsers.remove (u.uid); } sessions.remove (u.getSessionID ()); u.setNode (null); } } - // logEvent ("Cleaned up "+name+": " + sessions.size () + " sessions remaining"); + + IServer.getLogger().log ("Cleaned up "+name+": " + sessions.size () + " sessions remaining"); } catch (Exception cx) { - logEvent ("Error cleaning up sessions: "+cx); + IServer.getLogger().log ("Error cleaning up sessions: "+cx); cx.printStackTrace (); } - // check if we should call scheduler + long now = System.currentTimeMillis (); if (now - lastScheduler > scheduleSleep) { lastScheduler = now; - Object val = null; + ESValue val = null; try { - val = eval.invokeFunction ((INode) null, "scheduler", new Object[0]); - } catch (Exception ignore) {} + val = eval.invokeFunction ((INode) null, "scheduler", new ESValue[0]); + } catch (Exception ignore) {} try { - int ret = ((Number) val).intValue (); + int ret = val.toInt32 (); if (ret < 1000) scheduleSleep = 60000l; else scheduleSleep = ret; } catch (Exception ignore) {} - // logEvent ("Called scheduler for "+name+", will sleep for "+scheduleSleep+" millis"); + IServer.getLogger().log ("Called scheduler for "+name+", will sleep for "+scheduleSleep+" millis"); } - - // 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"); + IServer.getLogger().log ("Scheduler for "+name+" exiting"); } - /** - * This method is called after the type.properties files are read on all prototypes, or after one - * or more of the type properties have been re-read after an update, to let the DbMappings reestablish - * the relations among them according to their mappings. - */ public void rewireDbMappings () { for (Enumeration e=dbMappings.elements(); e.hasMoreElements(); ) { try { DbMapping m = (DbMapping) e.nextElement (); m.rewire (); - String typename = m.getTypeName (); - // set prototype hierarchy - if (!"hopobject".equalsIgnoreCase (typename) && !"global".equalsIgnoreCase (typename)) { - Prototype proto = (Prototype) typemgr.prototypes.get (typename); - if (proto != null) { - String protoname = m.getExtends (); - // only use hopobject prototype if we're scripting HopObjects, not - // java objects. - boolean isjava = isJavaPrototype (typename); - if (protoname == null && !isjava) - protoname = "hopobject"; - Prototype parentProto = (Prototype) typemgr.prototypes.get (protoname); - if (parentProto == null && !isjava) - parentProto = (Prototype) typemgr.prototypes.get ("hopobject"); - if (parentProto != null) - proto.setParentPrototype (parentProto); - } - } } catch (Exception x) { - logEvent ("Error rewiring DbMappings: "+x); + IServer.getLogger().log ("Error rewiring DbMappings: "+x); } } } - /** - * 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; - } - - /** - * 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; - } - - /** - * Return the name of this application - */ public String getName () { return name; } - /** - * Return the directory of this application - */ - public File getAppDir() { - return appDir; - } - - /** - * Get the DbMapping associated with a prototype name in this application - */ public DbMapping getDbMapping (String typename) { return typename == null ? null : (DbMapping) dbMappings.get (typename); } - /** - * Associate a DbMapping object with a prototype name for this application. - */ public void putDbMapping (String typename, DbMapping dbmap) { dbMappings.put (typename, dbmap); } - /** - * Proxy method to get a property from the applications properties. - */ - public String getProperty (String propname) { - return props.getProperty (propname); - } - - /** - * Proxy method to get a property from the applications properties. - */ - public String getProperty (String propname, String defvalue) { - return props.getProperty (propname, defvalue); - } - - public SystemProperties getProperties() { - return props; - } - - /** - * - */ - public int countThreads () { - return threadgroup.activeCount() -1; - } - - /** - * - */ - public int countEvaluators () { - return allThreads.size()-1 ; - } - - /** - * - */ - public int countFreeEvaluators () { - return freeThreads.size (); - } - - /** - * - */ - public int countActiveEvaluators () { - return allThreads.size() - freeThreads.size () -1; - } - - /** - * - */ - public int countMaxActiveEvaluators () { - // return typemgr.countRegisteredRequestEvaluators () -1; - // not available due to framework refactoring - return -1; - } - - /** - * - */ - public long getRequestCount () { - return requestCount; - } - - /** - * - */ - public long getXmlrpcCount () { - return xmlrpcCount; - } - - /** - * - */ - public long getErrorCount () { - return errorCount; - } /** * Periodically called to log thread stats for this application */ public void printThreadStats () { - logEvent ("Thread Stats for "+name+": "+threadgroup.activeCount()+" active"); - Runtime rt = Runtime.getRuntime (); + IServer.getLogger().log ("Thread Stats for "+name+": "+threadgroup.activeCount()+" active"); + Runtime rt = Runtime.getRuntime (); long free = rt.freeMemory (); long total = rt.totalMemory (); - logEvent ("Free memory: "+(free/1024)+" kB"); - logEvent ("Total memory: "+(total/1024)+" kB"); + IServer.getLogger().log ("Free memory: "+(free/1024)+" kB"); + IServer.getLogger().log ("Total memory: "+(total/1024)+" kB"); } /** @@ -1210,18 +552,18 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat } +////////////////////////////////////////////////////////////// +//// XML-RPC handler class + -/** - * XML-RPC handler class for this application. - */ class XmlRpcInvoker implements XmlRpcHandler { - + Application app; public XmlRpcInvoker (Application app) { - this.app = app; + this.app = app; } - + public Object execute (String method, Vector argvec) throws Exception { app.xmlrpcCount += 1; @@ -1230,7 +572,7 @@ class XmlRpcInvoker implements XmlRpcHandler { RequestEvaluator ev = null; try { ev = app.getEvaluator (); - retval = ev.invokeXmlRpc (method, argvec.toArray()); + retval = ev.invokeXmlRpc (method, argvec); } finally { app.releaseEvaluator (ev); } @@ -1239,9 +581,10 @@ class XmlRpcInvoker implements XmlRpcHandler { } -/** - * XML-RPC access permission checker - */ +////////////////////////////////////////////////////////////// +//// XML-RPC access permission checker + + class XmlRpcAccess { Application app; @@ -1265,11 +608,9 @@ class XmlRpcAccess { /* * create internal representation of XML-RPC-Permissions. They're encoded in the app property - * file like this: - * + * file like this: * xmlrpcAccess = root.sayHello, story.countMessages, user.login - * - * i.e. a prototype.method entry for each function callable via XML-RPC. + * i.e. a prototype.method entry for each function callable via XML-RPC. */ private void init () { String newAccessprop = app.props.getProperty ("xmlrpcaccess"); diff --git a/src/helma/scripting/fesi/ESAppNode.java b/src/helma/framework/core/ESAppNode.java similarity index 54% rename from src/helma/scripting/fesi/ESAppNode.java rename to src/helma/framework/core/ESAppNode.java index 83b3e15f..f80770fc 100644 --- a/src/helma/scripting/fesi/ESAppNode.java +++ b/src/helma/framework/core/ESAppNode.java @@ -1,14 +1,12 @@ // ESAppNode.java // Copyright (c) Hannes Wallnöfer 1998-2000 -package helma.scripting.fesi; +package helma.framework.core; -import helma.framework.core.*; import helma.objectmodel.*; import FESI.Exceptions.*; import FESI.Data.*; import FESI.Interpreter.Evaluator; -import java.util.Iterator; /** * ESApp represents the app node of an application, providing an app-wide transient shared @@ -20,18 +18,17 @@ public class ESAppNode extends ESNode { private Application app; private DatePrototype createtime; - public ESAppNode (INode node, FesiEvaluator eval) throws EcmaScriptException { - super (eval.getPrototype("hopobject"), eval.getEvaluator(), node, eval); - app = eval.getApplication(); - createtime = new DatePrototype (evaluator, node.created()); - FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); + public ESAppNode (INode node, RequestEvaluator eval) throws EcmaScriptException { + super (eval.esNodePrototype, eval.evaluator, node, eval); + app = eval.app; + createtime = new DatePrototype (eval.evaluator, node.created()); + FunctionPrototype fp = (FunctionPrototype) eval.evaluator.getFunctionPrototype(); putHiddenProperty("getThreads", new AppCountThreads ("getThreads", evaluator, fp)); putHiddenProperty("getMaxThreads", new AppCountEvaluators ("getMaxThreads", evaluator, fp)); putHiddenProperty("getFreeThreads", new AppCountFreeEvaluators ("getFreeThreads", evaluator, fp)); - putHiddenProperty("getActiveThreads", new AppCountActiveEvaluators ("getActiveThreads", evaluator, fp)); - putHiddenProperty("getMaxActiveThreads", new AppCountMaxActiveEvaluators ("getMaxActiveThreads", evaluator, fp)); + putHiddenProperty("getActiveThreads", new AppCountBusyEvaluators ("getActiveThreads", evaluator, fp)); + putHiddenProperty("getMaxActiveThreads", new AppCountMaxBusyEvaluators ("getMaxActiveThreads", evaluator, fp)); putHiddenProperty("setMaxThreads", new AppSetNumberOfEvaluators ("setMaxThreads", evaluator, fp)); - putHiddenProperty("clearCache", new AppClearCache ("clearCache", evaluator, fp)); } /** @@ -39,32 +36,17 @@ public class ESAppNode extends ESNode { */ public ESValue getProperty (String propname, int hash) throws EcmaScriptException { if ("requestCount".equals (propname)) { - return new ESNumber (app.getRequestCount ()); + return new ESNumber (app.requestCount); } if ("xmlrpcCount".equals (propname)) { - return new ESNumber (app.getXmlrpcCount ()); + return new ESNumber (app.xmlrpcCount); } if ("errorCount".equals (propname)) { - return new ESNumber (app.getErrorCount ()); + return new ESNumber (app.errorCount); } if ("upSince".equals (propname)) { return createtime; } - if ("skinfiles".equals (propname)) { - ESObject skinz = new ObjectPrototype (null, evaluator); - for (Iterator it = app.getPrototypes().iterator(); it.hasNext(); ) { - Prototype p = (Prototype) it.next (); - ESObject proto = new ObjectPrototype (null, evaluator); - for (Iterator it2 = p.skins.values().iterator(); it2.hasNext(); ) { - SkinFile sf = (SkinFile) it2.next (); - String name = sf.getName (); - Skin skin = sf.getSkin (); - proto.putProperty (name, new ESString (skin.getSource ()), name.hashCode ()); - } - skinz.putProperty (p.getName (), proto, p.getName ().hashCode ()); - } - return skinz; - } if ("__app__".equals (propname)) { return new ESWrapper (app, evaluator); } @@ -77,7 +59,7 @@ public class ESAppNode extends ESNode { super (fp, evaluator, name, 0); } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return new ESNumber (app.countEvaluators ()); + return new ESNumber (app.allThreads.size()-1); } } @@ -86,25 +68,25 @@ public class ESAppNode extends ESNode { super (fp, evaluator, name, 0); } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return new ESNumber (app.countFreeEvaluators ()); + return new ESNumber (app.freeThreads.size()); } } - class AppCountActiveEvaluators extends BuiltinFunctionObject { - AppCountActiveEvaluators (String name, Evaluator evaluator, FunctionPrototype fp) { + class AppCountBusyEvaluators extends BuiltinFunctionObject { + AppCountBusyEvaluators (String name, Evaluator evaluator, FunctionPrototype fp) { super (fp, evaluator, name, 0); } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return new ESNumber (app.countActiveEvaluators ()); + return new ESNumber (app.allThreads.size() - app.freeThreads.size() -1); } } - class AppCountMaxActiveEvaluators extends BuiltinFunctionObject { - AppCountMaxActiveEvaluators (String name, Evaluator evaluator, FunctionPrototype fp) { + class AppCountMaxBusyEvaluators extends BuiltinFunctionObject { + AppCountMaxBusyEvaluators (String name, Evaluator evaluator, FunctionPrototype fp) { super (fp, evaluator, name, 0); } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return new ESNumber (app.countMaxActiveEvaluators ()); + return new ESNumber (app.typemgr.countRegisteredRequestEvaluators () -1); } } @@ -113,7 +95,7 @@ public class ESAppNode extends ESNode { super (fp, evaluator, name, 0); } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return new ESNumber (app.countThreads ()); + return new ESNumber (app.threadgroup.activeCount() -1); } } @@ -125,25 +107,59 @@ public class ESAppNode extends ESNode { RequestEvaluator ev = new RequestEvaluator (app); if (arguments.length != 1) return ESBoolean.makeBoolean (false); - // add one to the number to compensate for the internal scheduler. return ESBoolean.makeBoolean (app.setNumberOfEvaluators (1 + arguments[0].toInt32())); } } - class AppClearCache extends BuiltinFunctionObject { - AppClearCache (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - app.clearCache (); - return ESBoolean.makeBoolean (true); - } - } public String toString () { - return ("AppNode "+node.getElementName ()); + return ("AppNode "+node.getNameOrID ()); } } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/scripting/fesi/ESNode.java b/src/helma/framework/core/ESNode.java similarity index 56% rename from src/helma/scripting/fesi/ESNode.java rename to src/helma/framework/core/ESNode.java index 85cef0a7..e6429661 100644 --- a/src/helma/scripting/fesi/ESNode.java +++ b/src/helma/framework/core/ESNode.java @@ -2,11 +2,9 @@ // Copyright (c) Hannes Wallnöfer 1998-2000 -package helma.scripting.fesi; +package helma.framework.core; import helma.objectmodel.*; -import helma.objectmodel.db.*; -import helma.framework.core.*; import helma.util.*; import FESI.Interpreter.*; import FESI.Exceptions.*; @@ -24,106 +22,158 @@ import java.util.*; public class ESNode extends ObjectPrototype { 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; - // The handle of the wrapped Node. Makes ESNodes comparable without accessing the wrapped node. - NodeHandle handle; + // The ID of the wrapped Node. Makes ESNodes comparable without accessing the wrapped node. + String nodeID; DbMapping dbmap; + ESObject cacheWrapper; Throwable lastError = null; - FesiEvaluator eval; + RequestEvaluator eval; - /** - * Constructor used to create transient cache nodes - */ - public ESNode (INode node, FesiEvaluator eval) { - super (eval.getPrototype("hopobject"), eval.getEvaluator()); - this.eval = eval; - this.node = node; - cache = null; - cacheWrapper = null; + // used to create cache nodes + protected ESNode (INode node, RequestEvaluator eval) { + super (eval.esNodePrototype, eval.evaluator); + this.eval = eval; + this.node = node; + cache = null; - // this is a transient node, set node handle to null - handle = null; + cacheWrapper = null; + nodeID = node.getID (); + dbmap = node.getDbMapping (); } + + public ESNode (ESObject prototype, Evaluator evaluator, Object obj, RequestEvaluator eval) { + super (prototype, evaluator); + // IServer.getLogger().log ("in ESNode constructor: "+o.getClass ()); + this.eval = eval; + if (obj == null) + node = new Node (); + else if (obj instanceof ESWrapper) + node = (INode) ((ESWrapper) obj).getJavaObject (); + else if (obj instanceof INode) + node = (INode) obj; + else + node = new Node (obj.toString ()); + // set nodeID to id of wrapped node + nodeID = node.getID (); + dbmap = node.getDbMapping (); - public ESNode (ESObject prototype, Evaluator evaluator, Object obj, FesiEvaluator eval) { - super (prototype, evaluator); - // eval.app.logEvent ("in ESNode constructor: "+o.getClass ()); - this.eval = eval; - 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; + // get transient cache Node + cache = node.getCacheNode (); + cacheWrapper = new ESNode (cache, eval); } /** * 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 () { + private void checkNode () { if (node.getState () == INode.INVALID) try { - node = handle.getNode (eval.app.getWrappedNodeManager ()); + setNode (eval.app.nmgr.getNode (node.getID (), node.getDbMapping ())); } catch (Exception nx) {} } - public INode getNode () { - checkNode (); - return node; + public INode getNode () { + checkNode (); + return node; + } + + public void setNode (INode node) { + if (node != null) { + this.node = node; + nodeID = node.getID (); + dbmap = node.getDbMapping (); + eval.objectcache.put (node, this); + // get transient cache Node + cache = node.getCacheNode (); + cacheWrapper = new ESNode (cache, eval); + } } public void setPrototype (String protoName) { - checkNode (); - node.setPrototype (protoName); + checkNode (); + node.setPrototype (protoName); } public String getPrototypeName () { - return node.getPrototype (); + return node.getPrototype (); } public String getESClassName () { - return "HopObject"; + return "HopObject"; } public String toString () { - if (node == null) - return ""; - return node.toString (); + if (node == null) + return ""; + return node.toString (); } public String toDetailString () { - return "ES:[Object: builtin " + this.getClass().getName() + ":" + - ((node == null) ? "null" : node.toString()) + "]"; + return "ES:[Object: builtin " + this.getClass().getName() + ":" + + ((node == null) ? "null" : node.toString()) + "]"; } protected void setError (Throwable e) { - lastError = e; + lastError = e; } + public boolean setContent (ESValue what[]) { + checkNode (); + if (what.length > 0) { + if (what[0] instanceof ESString) { + node.setContent (what[0].toString ()); + return true; + } + if (what[0] instanceof ESWrapper) { + Object o = ((ESWrapper) what[0]).toJavaObject (); + if (o instanceof INode) { + try { + INode p = (INode) o; + node.setContent (p.getContent (), p.getContentType ()); + return true; + } catch (Exception x) { + IServer.getLogger().log ("error in ESNode.setContent: "+x); + } + } + } + if (what[0] instanceof ESNode) { + INode i = ((ESNode) what[0]).getNode (); + try { + node.setContent (i.getContent (), i.getContentType ()); + return true; + } catch (Exception x) { + IServer.getLogger().log ("error in ESNode.setContent: "+x); + } + } + } + return false; + } + + public Object getContent () { + checkNode (); + if (node.getContentLength () == 0) + return null; + String contentType = node.getContentType (); + if (contentType != null && contentType.startsWith ("text/")) { + return node.getText (); + } else { + return node.getContent (); + } + } + public boolean add (ESValue what[]) { checkNode (); for (int i=0; i "+esn); + if (esn != null) { + esn.rewrap (newnode.getSubnodeAt (i)); + } + } + for (Enumeration e=oldnode.properties (); e.hasMoreElements (); ) { + IProperty p = oldnode.get ((String) e.nextElement (), false); + if (p != null && p.getType () == IProperty.NODE) { + INode next = p.getNodeValue (); + ESNode esn = eval.getNodeWrapperFromCache (next); + if (esn != null) { + esn.rewrap (newnode.getNode (p.getName (), false)); + } + } + } + } /** - * Remove node itself or one or more subnodes. + * Remove one or more subnodes. */ public boolean remove (ESValue args[]) { checkNode (); - // semantics: if called without arguments, remove self. - // otherwise, remove given subnodes. - if (args.length == 0) { - return node.remove (); - } else { - for (int i=0; i + * This means that the wrapped node will be swapped when the user logs in or out. + * To save session state across logins and logouts, the + * cache property of the user object stays the same for the whole time the user + * spends on this site. + */ + +public class ESUser extends ESNode { + + // if the user is online, this is his/her online session object + public User user; + + public ESUser (INode node, RequestEvaluator eval) { + super (eval.esUserPrototype, eval.evaluator, node, eval); + user = (User) eval.app.activeUsers.get (node.getNameOrID ()); + if (user == null) + user = (User) eval.app.sessions.get (node.getNameOrID ()); + if (user != null) { + cache = user.cache; + cacheWrapper = new ESNode (cache, eval); + } + } + + /** + * Overrides getProperty to return the uid (which is not a regular property) + */ + public ESValue getProperty (String propname, int hash) throws EcmaScriptException { + if ("uid".equals (propname)) { + if (user == null || user.uid == null) + return ESNull.theNull; + else + return new ESString (user.uid); + } + if ("sessionID".equals (propname)) { + return new ESString (user.getSessionID ()); + } + return super.getProperty (propname, hash); + } + + + public void setUser (User user) { + if (this.user != user) { + this.user = user; + cache = user.cache; + } + cacheWrapper = new ESNode (cache, eval); + } + + public void setNode (INode node) { + if (node != null) { + this.node = node; + nodeID = node.getID (); + dbmap = node.getDbMapping (); + eval.objectcache.put (node, this); + // we don't take over the transient cache from the node, + // because we always use the one from the user object. + } + } + + + public String toString () { + return ("UserObject "+node.getNameOrID ()); + } + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/framework/core/FunctionFile.java b/src/helma/framework/core/FunctionFile.java new file mode 100644 index 00000000..32337bb9 --- /dev/null +++ b/src/helma/framework/core/FunctionFile.java @@ -0,0 +1,120 @@ +// FunctionFile.java +// Copyright (c) Hannes Wallnöfer 1998-2000 + +package helma.framework.core; + +import java.util.*; +import java.io.*; +import helma.framework.*; +import helma.objectmodel.IServer; +import FESI.Data.ObjectPrototype; +import FESI.Exceptions.EcmaScriptException; +import FESI.Interpreter.*; + + +/** + * This represents a File containing JavaScript functions for a given Object. + */ + + +public class FunctionFile { + + String name; + Prototype prototype; + Application app; + File file; + long lastmod; + + public FunctionFile (File file, String name, Prototype proto) { + this.prototype = proto; + this.app = proto.app; + this.name = name; + this.file = file; + update (file); + } + + + public void update (File f) { + + this.file = f; + + long fmod = file.lastModified (); + if (lastmod == fmod) + return; + + lastmod = fmod; + // app.typemgr.readFunctionFile (file, prototype.getName ()); + Iterator evals = app.typemgr.getRegisteredRequestEvaluators (); + while (evals.hasNext ()) { + try { + RequestEvaluator reval = (RequestEvaluator) evals.next (); + updateRequestEvaluator (reval); + } catch (Exception ignore) {} + } + + } + + public synchronized void updateRequestEvaluator (RequestEvaluator reval) { + + EvaluationSource es = new FileEvaluationSource(file.getPath(), null); + FileReader fr = null; + + try { + fr = new FileReader(file); + ObjectPrototype op = reval.getPrototype (prototype.getName()); + reval.evaluator.evaluate(fr, op, es, false); + } catch (IOException e) { + IServer.getLogger().log ("Error parsing function file "+app.getName()+":"+prototype.getName()+"/"+file.getName()+": "+e); + } catch (EcmaScriptException e) { + IServer.getLogger().log ("Error parsing function file "+app.getName()+":"+prototype.getName()+"/"+file.getName()+": "+e); + } finally { + if (fr!=null) { + try { + fr.close(); + } catch (IOException ignore) {} + } + } + + } + + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/scripting/fesi/HopExtension.java b/src/helma/framework/core/HopExtension.java similarity index 72% rename from src/helma/scripting/fesi/HopExtension.java rename to src/helma/framework/core/HopExtension.java index be0a8653..b7425f7c 100644 --- a/src/helma/scripting/fesi/HopExtension.java +++ b/src/helma/framework/core/HopExtension.java @@ -1,13 +1,10 @@ // HopExtension.java // Copyright (c) Hannes Wallnöfer 1998-2000 -package helma.scripting.fesi; +package helma.framework.core; -import helma.framework.*; -import helma.framework.core.*; import helma.objectmodel.*; import helma.util.*; -import helma.framework.IPathElement; import FESI.Interpreter.*; import FESI.Exceptions.*; import FESI.Extensions.*; @@ -17,34 +14,33 @@ import java.util.*; import java.text.*; import org.xml.sax.InputSource; -/** - * 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 user prototype, the global prototype etc. +/** + * This is the basic Extension for FESI interpreters used in HOP. It sets up + * varios constructors, global functions and properties etc. */ - + public class HopExtension { protected Application app; - protected FesiEvaluator fesi; - public HopExtension (Application app) { - this.app = app; + public HopExtension () { + super(); } /** * Called by the evaluator after the extension is loaded. */ - public void initializeExtension (FesiEvaluator fesi) throws EcmaScriptException { - this.fesi = fesi; - Evaluator evaluator = fesi.getEvaluator (); + public void initializeExtension (RequestEvaluator reval) throws EcmaScriptException { + + this.app = reval.app; + Evaluator evaluator = reval.evaluator; GlobalObject go = evaluator.getGlobalObject(); FunctionPrototype fp = (FunctionPrototype) evaluator.getFunctionPrototype(); ESObject op = evaluator.getObjectPrototype(); - // The editor functions for String, Boolean and Number are deprecated! + ////////// 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 (); @@ -55,87 +51,96 @@ public class HopExtension { ESObject dp = evaluator.getDatePrototype (); dp.putHiddenProperty ("format", new DatePrototypeFormat ("format", evaluator, fp)); - sp.putHiddenProperty ("trim", new StringTrim ("trim", evaluator, fp)); + reval.esNodePrototype = new ObjectPrototype(op, evaluator); // the Node prototype - // generic (Java wrapper) object prototype - ObjectPrototype esObjectPrototype = new ObjectPrototype (op, evaluator); - // the Node prototype - ObjectPrototype esNodePrototype = new ObjectPrototype(op, evaluator); - // the User prototype - ObjectPrototype esUserPrototype = new ObjectPrototype (esNodePrototype, evaluator); - // the Node constructor - ESObject node = new NodeConstructor ("Node", fp, fesi); + reval.esUserPrototype = new ObjectPrototype (reval.esNodePrototype, evaluator); // the User prototype + + ESObject node = new NodeConstructor ("Node", fp, reval); // the Node constructor // 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 ("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)); + reval.esNodePrototype.putHiddenProperty ("add", new NodeAdd ("add", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("addAt", new NodeAddAt ("addAt", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("remove", new NodeRemove ("remove", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("link", new NodeLink ("link", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("list", new NodeList ("list", evaluator, fp, reval)); + reval.esNodePrototype.putHiddenProperty ("set", new NodeSet ("set", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("get", new NodeGet ("get", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("count", new NodeCount ("count", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("contains", new NodeContains ("contains", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("size", new NodeCount ("size", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("editor", new NodeEditor ("editor", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("chooser", new NodeChooser ("chooser", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("multiChooser", new MultiNodeChooser ("multiChooser", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("getContent", new NodeGetContent ("getContent", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("setContent", new NodeSetContent ("setContent", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("path", new NodePath ("path", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("href", new NodeHref ("href", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("setParent", new NodeSetParent ("setParent", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty ("invalidate", new NodeInvalidate ("invalidate", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, reval, false, false)); + reval.esNodePrototype.putHiddenProperty("renderSkin_as_string", new RenderSkin ("renderSkin_as_string", evaluator, fp, reval, 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("getUser", new GlobalGetUser ("getUser", evaluator, fp)); - go.putHiddenProperty("getUserBySession", new GlobalGetUserBySession ("getUserBySession", evaluator, fp)); - go.putHiddenProperty("getAllUsers", new GlobalGetAllUsers ("getAllUsers", evaluator, fp)); - go.putHiddenProperty("getActiveUsers", new GlobalGetActiveUsers ("getActiveUsers", evaluator, fp)); - go.putHiddenProperty("countActiveUsers", new GlobalCountActiveUsers ("countActiveUsers", evaluator, fp)); + go.putHiddenProperty("getUser", new GlobalGetUser ("getUser", evaluator, fp, reval)); + go.putHiddenProperty("getUserBySession", new GlobalGetUserBySession ("getUserBySession", evaluator, fp, reval)); + go.putHiddenProperty("getAllUsers", new GlobalGetAllUsers ("getAllUsers", evaluator, fp, reval)); + go.putHiddenProperty("getActiveUsers", new GlobalGetActiveUsers ("getActiveUsers", evaluator, fp, reval)); go.putHiddenProperty("isActive", new GlobalIsActive ("isActive", 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("getSkin", new GlobalGetSkin ("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("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, reval, true, false)); + go.putHiddenProperty("renderSkin_as_string", new RenderSkin ("renderSkin_as_string", evaluator, fp, reval, true, true)); go.deleteProperty("exit", "exit".hashCode()); // and some methods for session management from JS... - esUserPrototype.putHiddenProperty("logon", new UserLogin ("logon", evaluator, fp)); - esUserPrototype.putHiddenProperty("login", new UserLogin ("login", evaluator, fp)); - esUserPrototype.putHiddenProperty("register", new UserRegister ("register", evaluator, fp)); - esUserPrototype.putHiddenProperty("logout", new UserLogout ("logout", evaluator, fp)); - esUserPrototype.putHiddenProperty("onSince", new UserOnSince ("onSince", evaluator, fp)); - esUserPrototype.putHiddenProperty("lastActive", new UserLastActive ("lastActive", evaluator, fp)); - esUserPrototype.putHiddenProperty("touch", new UserTouch ("touch", evaluator, fp)); + reval.esUserPrototype.putHiddenProperty("logon", new UserLogin ("logon", evaluator, fp)); + reval.esUserPrototype.putHiddenProperty("login", new UserLogin ("login", evaluator, fp)); + reval.esUserPrototype.putHiddenProperty("register", new UserRegister ("register", evaluator, fp, reval)); + reval.esUserPrototype.putHiddenProperty("logout", new UserLogout ("logout", evaluator, fp)); + reval.esUserPrototype.putHiddenProperty("onSince", new UserOnSince ("onSince", evaluator, fp)); + reval.esUserPrototype.putHiddenProperty("lastActive", new UserLastActive ("lastActive", evaluator, fp)); + reval.esUserPrototype.putHiddenProperty("touch", new UserTouch ("touch", evaluator, fp)); - // register object prototypes with FesiEvaluator - fesi.putPrototype ("global", go); - fesi.putPrototype ("hopobject", esNodePrototype); - fesi.putPrototype ("__javaobject__", esObjectPrototype); - fesi.putPrototype ("user", esUserPrototype); } + + class NodeSetContent extends BuiltinFunctionObject { + NodeSetContent (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.setContent (arguments)); + } + } + + class NodeGetContent extends BuiltinFunctionObject { + NodeGetContent (String name, Evaluator evaluator, FunctionPrototype fp) { + super (fp, evaluator, name, 1); + } + public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + ESNode node = (ESNode) thisObject; + Object content = node.getContent (); + if (content == null) + return ESNull.theNull; + else + return ESLoader.normalizeValue (content, this.evaluator); + } + } + class NodeAdd extends BuiltinFunctionObject { NodeAdd (String name, Evaluator evaluator, FunctionPrototype fp) { super (fp, evaluator, name, 1); @@ -178,8 +183,10 @@ public class HopExtension { } class NodeList extends BuiltinFunctionObject { - NodeList (String name, Evaluator evaluator, FunctionPrototype fp) { + RequestEvaluator reval; + NodeList (String name, Evaluator evaluator, FunctionPrototype fp, RequestEvaluator reval) { super(fp, evaluator, name, 0); + this.reval = reval; } public ESValue callFunction(ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { ESNode node = (ESNode) thisObject; @@ -198,10 +205,7 @@ public class HopExtension { 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); + esv = thisObject.getProperty (name, name.hashCode ()); } return (esv); } @@ -253,7 +257,10 @@ public class HopExtension { INode node = esn.getNode (); if (node instanceof helma.objectmodel.db.Node) { ((helma.objectmodel.db.Node) node).invalidate (); - esn.checkNode (); + try { + node = app.nmgr.getNode (node.getID (), node.getDbMapping ()); + esn.setNode (node); + } catch (Exception x) {} } return ESBoolean.makeBoolean (true); } @@ -406,21 +413,51 @@ public class HopExtension { } } - 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 NodeChooser extends BuiltinFunctionObject { + NodeChooser (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.length < 1) { + return ESBoolean.makeBoolean(false); + } + String nodename = arguments[0].toString (); + INode target = esn.getNode ().getNode (nodename, false); + ESNode collection = arguments.length > 1 ? (ESNode) arguments[1] : esn; + if (arguments.length > 2) + return new ESString (getNodeChooserDD (nodename, collection.getNode (), target, arguments[2].toString ())); + else + return new ESString (getNodeChooserRB (nodename, collection.getNode (), target)); + } + } + + class MultiNodeChooser extends BuiltinFunctionObject { + MultiNodeChooser (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.length < 1) { + return ESBoolean.makeBoolean(false); + } + String nodename = arguments[0].toString (); + INode thisNode = esn.getNode (); + INode target = thisNode.getNode (nodename, false); + if (target == null) { + target = thisNode.createNode (nodename); + } + ESNode collection = arguments.length > 1 ? (ESNode) arguments[1] : esn; + return new ESString (getNodeChooserCB (nodename, collection.getNode (), target)); + } + } class UserLogin extends BuiltinFunctionObject { UserLogin (String name, Evaluator evaluator, FunctionPrototype fp) { super (fp, evaluator, name, 1); } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - if (arguments.length < 2) + if (arguments.length < 2) return ESBoolean.makeBoolean(false); ESUser u = (ESUser) thisObject; if (u.user == null) @@ -434,8 +471,10 @@ public class HopExtension { } class UserRegister extends BuiltinFunctionObject { - UserRegister (String name, Evaluator evaluator, FunctionPrototype fp) { + RequestEvaluator reval; + UserRegister (String name, Evaluator evaluator, FunctionPrototype fp, RequestEvaluator reval) { super (fp, evaluator, name, 2); + this.reval = reval; } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { if (arguments.length < 2) @@ -444,7 +483,7 @@ public class HopExtension { if (unode == null) return ESNull.theNull; else - return fesi.getNodeWrapper (unode); + return reval.getNodeWrapper (unode); } } @@ -471,7 +510,7 @@ public class HopExtension { ESUser u = (ESUser) thisObject; if (u.user == null) throw new EcmaScriptException ("onSince() can only be called for users that are online at the moment!"); - DatePrototype date = new DatePrototype(this.evaluator, new Date (u.user.onSince ())); + DatePrototype date = new DatePrototype(this.evaluator, new Date (u.user.onSince)); return date; } } @@ -484,7 +523,7 @@ public class HopExtension { ESUser u = (ESUser) thisObject; if (u.user == null) throw new EcmaScriptException ("lastActive() can only be called for users that are online at the moment!"); - DatePrototype date = new DatePrototype(this.evaluator, new Date (u.user.lastTouched ())); + DatePrototype date = new DatePrototype(this.evaluator, new Date (u.user.lastTouched)); return date; } } @@ -509,20 +548,28 @@ public class HopExtension { if (arguments.length == 0) return new ESString (""); String defval = (arguments.length > 1) ? arguments[1].toString () : ""; - return new ESString (app.getProperty (arguments[0].toString (), defval)); + return new ESString (app.props.getProperty (arguments[0].toString (), defval)); } } - class GlobalAuthenticate extends BuiltinFunctionObject { - GlobalAuthenticate (String name, Evaluator evaluator, FunctionPrototype fp) { + /** + * Get a parsed Skin from the central skin cache if possible, otherwise create it + */ + class GlobalGetSkin extends BuiltinFunctionObject { + GlobalGetSkin (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 ())); - + if (arguments.length != 1 || ESNull.theNull.equals (arguments[0])) + throw new EcmaScriptException ("getSkin must be called with one String argument!"); + String str = arguments[0].toString (); + Skin skin = (Skin) app.skincache.get (str); + if (skin == null) { + skin = new Skin (str); + app.skincache.put (str, skin); + } + return new ESWrapper (skin, evaluator); } } @@ -535,14 +582,8 @@ public class HopExtension { } 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 = (Skin) app.skincache.get (str); - if (skin == null) { - skin = new Skin (str, app); - app.skincache.put (str, skin); - } - return new ESWrapper (skin, evaluator); + throw new EcmaScriptException ("createSkin must be called with one String argument!"); + return new ESWrapper (new Skin (arguments[0].toString()), evaluator); } } @@ -550,29 +591,25 @@ public class HopExtension { * Render a skin */ class RenderSkin extends BuiltinFunctionObject { + RequestEvaluator reval; boolean global; boolean asString; - RenderSkin (String name, Evaluator evaluator, FunctionPrototype fp, boolean global, boolean asString) { + RenderSkin (String name, Evaluator evaluator, FunctionPrototype fp, + RequestEvaluator reval, boolean global, boolean asString) { super (fp, evaluator, name, 1); + this.reval = reval; this.global = global; this.asString = asString; } - public ESValue callFunction (ESObject thisObj, ESValue[] arguments) throws EcmaScriptException { + public ESValue callFunction (ESObject thisObject, 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"); + throw new EcmaScriptException ("renderSkin must be called with one Skin argument and an optional parameter 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 - ESObject paramObject = (ESObject) arguments[1]; - params = new HashMap (); - for (Enumeration en=paramObject.getProperties(); en.hasMoreElements(); ) { - String propname = (String) en.nextElement(); - params.put (propname, paramObject.getProperty (propname, propname.hashCode()).toJavaObject()); - } - } + Skin skin = null; + ESNode handlerNode = global ? null : (ESNode) thisObject; + ESObject paramObject = null; + if (arguments.length > 1 && arguments[1] instanceof ESObject) + paramObject = (ESObject) arguments[1]; // 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) { @@ -581,41 +618,16 @@ public class HopExtension { skin = (Skin) obj; } - // if res.skinpath is set, transform it into an array of java objects - // (strings for directory names and INodes for internal, db-stored skinsets) - ResponseTrans res = fesi.getResponse(); - Object[] skinpath = res.getTranslatedSkinpath (); - if (skinpath == null) { - skinpath = new Object[0]; - Object rawSkinpath = res.getSkinpath (); - if (rawSkinpath != null && rawSkinpath instanceof JSWrapper) { - JSWrapper jsw = (JSWrapper) rawSkinpath; - ESObject eso = jsw.getESObject (); - if (eso instanceof ArrayPrototype) { - ArrayPrototype array = (ArrayPrototype) eso; - skinpath = new Object[array.size()]; - for (int i=0; i 0) { - String uname = arguments[0].toString ().trim (); - user = app.getUserNode (uname); - } - if (user == null) - return ESNull.theNull; - return fesi.getNodeWrapper (user); + INode user = null; + if (arguments.length > 0) { + String uname = arguments[0].toString ().trim (); + user = app.getUserNode (uname); + } + if (user == null) + return ESNull.theNull; + return reval.getNodeWrapper (user); } } class GlobalGetUserBySession extends BuiltinFunctionObject { - GlobalGetUserBySession (String name, Evaluator evaluator, FunctionPrototype fp) { + RequestEvaluator reval; + GlobalGetUserBySession (String name, Evaluator evaluator, FunctionPrototype fp, RequestEvaluator reval) { super (fp, evaluator, name, 1); + this.reval = reval; } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - User user = null; - if (arguments.length > 0) { - String sid = arguments[0].toString ().trim (); - user = app.getUser (sid); - } - if (user == null || user.getUID() == null) - return ESNull.theNull; - user.touch (); - return fesi.getNodeWrapper (user.getNode ()); + User user = null; + if (arguments.length > 0) { + String sid = arguments[0].toString ().trim (); + user = app.getUser (sid); + } + if (user == null || user.uid == null) + return ESNull.theNull; + user.touch (); + return reval.getNodeWrapper (user.getNode ()); } } class GlobalGetAllUsers extends BuiltinFunctionObject { - GlobalGetAllUsers (String name, Evaluator evaluator, FunctionPrototype fp) { + RequestEvaluator reval; + GlobalGetAllUsers (String name, Evaluator evaluator, FunctionPrototype fp, RequestEvaluator reval) { super (fp, evaluator, name, 1); + this.reval = reval; } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { INode users = app.getUserRoot (); @@ -678,37 +696,30 @@ public class HopExtension { } class GlobalGetActiveUsers extends BuiltinFunctionObject { - GlobalGetActiveUsers (String name, Evaluator evaluator, FunctionPrototype fp) { + RequestEvaluator reval; + GlobalGetActiveUsers (String name, Evaluator evaluator, FunctionPrototype fp, RequestEvaluator reval) { super (fp, evaluator, name, 1); + this.reval = reval; } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { Hashtable sessions = (Hashtable) app.sessions.clone (); + int l = sessions.size (); ESObject ap = this.evaluator.getArrayPrototype(); ArrayPrototype theArray = new ArrayPrototype (ap, this.evaluator); - theArray.setSize (sessions.size ()); + theArray.setSize(l); int i=0; - // Hashtable visited = new Hashtable (); + Hashtable visited = new Hashtable (); for (Enumeration e=sessions.elements(); e.hasMoreElements(); ) { User u = (User) e.nextElement (); - // Note: we previously sorted out duplicate users - now we simply enumerate all active sessions. - // if (u.uid == null || !visited.containsKey (u.uid)) { - theArray.setElementAt (fesi.getNodeWrapper (u), i++); - // if (u.uid != null) visited.put (u.uid, u); - // } + if (u.uid == null || !visited.containsKey (u.uid)) { + theArray.setElementAt (reval.getNodeWrapper (u.getNode ()), i++); + if (u.uid != null) visited.put (u.uid, u); + } } return theArray; } } - class GlobalCountActiveUsers extends BuiltinFunctionObject { - GlobalCountActiveUsers (String name, Evaluator evaluator, FunctionPrototype fp) { - super (fp, evaluator, name, 1); - } - public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { - return new ESNumber (app.sessions.size ()); - } - } - class GlobalIsActive extends BuiltinFunctionObject { GlobalIsActive (String name, Evaluator evaluator, FunctionPrototype fp) { super (fp, evaluator, name, 1); @@ -759,7 +770,7 @@ public class HopExtension { return new ESString (age.toString ()); } catch (Exception e) { - app.logEvent ("Error formatting date: "+e); + IServer.getLogger().log ("Error formatting date: "+e); e.printStackTrace (); return new ESString (""); } @@ -810,17 +821,6 @@ public class HopExtension { } } - class GlobalEncodeForm extends BuiltinFunctionObject { - 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 ())); - } - } - // strip tags from XML/HTML text class GlobalStripTags extends BuiltinFunctionObject { GlobalStripTags (String name, Evaluator evaluator, FunctionPrototype fp) { @@ -865,7 +865,7 @@ public class HopExtension { Object p = arguments[0].toJavaObject (); if (p instanceof String) try { // first try to interpret string as URL - java.net.URL u = new java.net.URL (p.toString ()); + java.net.URL u = new java.net.URL (p.toString ()); parser.parse (p.toString()); } catch (java.net.MalformedURLException nourl) { // if not a URL, maybe it is the XML itself @@ -877,7 +877,7 @@ public class HopExtension { parser.parse (new InputSource ((Reader) p)); return ESLoader.normalizeObject (parser.getDocument(), evaluator); } catch (Exception noluck) { - app.logEvent ("Error creating XML document: "+noluck); + IServer.getLogger().log ("Error creating XML document: "+noluck); } return ESNull.theNull; } @@ -894,7 +894,7 @@ public class HopExtension { Object p = arguments[0].toJavaObject (); if (p instanceof String) try { // first try to interpret string as URL - java.net.URL u = new java.net.URL (p.toString ()); + java.net.URL u = new java.net.URL (p.toString ()); parser.parse (p.toString()); } catch (java.net.MalformedURLException nourl) { // if not a URL, maybe it is the HTML itself @@ -906,7 +906,7 @@ public class HopExtension { parser.parse (new InputSource ((Reader) p)); return ESLoader.normalizeObject (parser.getDocument(), evaluator); } catch (Exception noluck) { - app.logEvent ("Error creating HTML document: "+noluck); + IServer.getLogger().log ("Error creating HTML document: "+noluck); } return ESNull.theNull; } @@ -924,12 +924,24 @@ public class HopExtension { org.jdom.input.DOMBuilder builder = new org.jdom.input.DOMBuilder (); return ESLoader.normalizeObject (builder.build (doc), evaluator); } catch (Exception noluck) { - app.logEvent ("Error wrapping JDOM document: "+noluck); + IServer.getLogger().log ("Error wrapping JDOM document: "+noluck); } return ESNull.theNull; } } + + class NodePath extends BuiltinFunctionObject { + NodePath (String name, Evaluator evaluator, FunctionPrototype fp) { + super (fp, evaluator, name, 1); + } + public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + INode n = ((ESNode) thisObject).getNode (); + String tmpname = arguments[0].toString (); + return new ESString (app.getNodePath (n, tmpname)); + } + } + class NodeSetParent extends BuiltinFunctionObject { NodeSetParent (String name, Evaluator evaluator, FunctionPrototype fp) { super (fp, evaluator, name, 2); @@ -940,52 +952,16 @@ public class HopExtension { } } - class NodeClearCache extends BuiltinFunctionObject { - 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 ()); - } - } class NodeHref extends BuiltinFunctionObject { 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 (); + INode n = ((ESNode) thisObject).getNode (); String tmpname = arguments.length == 0 ? "" : arguments[0].toString (); - String basicHref =app.getNodeHref (elem, tmpname); - String hrefSkin = app.getProperty ("hrefSkin", null); - // FIXME: we should actually walk down the path from the object we called href() on - // instead we move down the URL path. - 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; - while (skin == null && skinElem != null) { - Prototype proto = app.getPrototype (skinElem); - if (proto != null) - skin = proto.getSkin (hrefSkin); - if (skin == null) - skinElem = app.getParentElement (skinElem); - } - - if (skin != null) { - return renderSkin (skin, basicHref, skinElem); - } - } - return new ESString (basicHref); - } - private ESString renderSkin (Skin skin, String path, Object skinElem) throws EcmaScriptException { - fesi.getResponse().pushStringBuffer (); - HashMap param = new HashMap (); - param.put ("path", path); - skin.render (fesi.getRequestEvaluator(), skinElem, param); - return new ESString (fesi.getResponse().popStringBuffer ().trim ()); + return new ESString (app.getNodeHref (n, tmpname)); + } } @@ -1002,7 +978,7 @@ public class HopExtension { double d = from <= to ? 1.0 : -1.0; for (double i=from; i*d<=to*d; i+=step) { if (Math.abs (i%1) < l) { - int j = (int) i; + int j = (int) i; b.append ("


"); + } catch (Exception x) { + IServer.getLogger().log ("Error building function protos in prototype: "+x); + // TypeManager.broadcaster.broadcast ("Error updating prototype "+name+" in application "+app.getName()+":
"+x.getMessage ()+"

"); + } + retval = true; + } + } + } + return retval; + } + public String getName () { return name; } - Updatable[] upd = null; - public Updatable[] getUpdatables () { - if (upd == null) { - upd = new Updatable[updatables.size()]; - int i = 0; - for (Iterator it = updatables.values().iterator(); it.hasNext(); ) { - upd[i++] = (Updatable) it.next(); - } + + public void initRequestEvaluator (RequestEvaluator reval) { + ObjectPrototype op = null; + if ("user".equalsIgnoreCase (name)) + op = reval.esUserPrototype; + else if ("global".equalsIgnoreCase (name)) + op = reval.global; + else if ("hopobject".equalsIgnoreCase (name)) + op = reval.esNodePrototype; + else { + op = new ObjectPrototype (reval.esNodePrototype, reval.evaluator); + try { + op.putProperty ("prototypename", new ESString (name), "prototypename".hashCode ()); + } catch (EcmaScriptException ignore) {} + } + reval.putPrototype (name, op); + + // Register a constructor for all types except global. + // This will first create a node and then call the actual (scripted) constructor on it. + if (!"global".equalsIgnoreCase (name)) { + try { + FunctionPrototype fp = (FunctionPrototype) reval.evaluator.getFunctionPrototype(); + reval.global.putHiddenProperty (name, new NodeConstructor (name, fp, reval)); + } catch (EcmaScriptException ignore) {} + } + + for (Enumeration en = functions.elements(); en.hasMoreElements(); ) { + FunctionFile ff = (FunctionFile) en.nextElement (); + ff.updateRequestEvaluator (reval); + } + for (Enumeration en = templates.elements(); en.hasMoreElements(); ) { + Template tmp = (Template) en.nextElement (); + try { + tmp.updateRequestEvaluator (reval); + } catch (EcmaScriptException ignore) {} + } + for (Enumeration en = actions.elements(); en.hasMoreElements(); ) { + Action act = (Action) en.nextElement (); + try { + act.updateRequestEvaluator (reval); + } catch (EcmaScriptException ignore) {} } - return upd; - } - - - - public String toString () { - return "[Prototype "+ app.getName()+"/"+name+"]"; - } - } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/framework/core/RequestEvaluator.java b/src/helma/framework/core/RequestEvaluator.java index 96ce003c..c72a7f32 100644 --- a/src/helma/framework/core/RequestEvaluator.java +++ b/src/helma/framework/core/RequestEvaluator.java @@ -3,52 +3,70 @@ package helma.framework.core; -import helma.objectmodel.*; -import helma.objectmodel.db.*; -import helma.framework.*; -import helma.scripting.*; -import helma.util.*; import java.util.*; +import java.io.*; +import helma.objectmodel.*; +import helma.objectmodel.db.Transactor; +import helma.framework.*; +import helma.framework.extensions.*; +import helma.xmlrpc.fesi.*; +import helma.util.*; +import Acme.LruHashtable; +import FESI.Data.*; +import FESI.Interpreter.*; +import FESI.Exceptions.*; /** - * This class does the work for incoming requests. It holds a transactor thread - * and an EcmaScript evaluator to get the work done. Incoming threads are + * This class does the work for incoming requests. It holds a transactor thread + * and an EcmaScript evaluator to get the work done. Incoming threads are * blocked until the request has been serviced by the evaluator, or the timeout * specified by the application has passed. In the latter case, the evaluator thread - * is killed and an error message is returned. + * is killed and an error message is returned. */ public class RequestEvaluator implements Runnable { - public Application app; + Application app; protected boolean initialized; - public RequestTrans req; - public ResponseTrans res; + RequestTrans req; + ResponseTrans res; volatile Transactor rtx; - // the object on which to invoke a function, if specified - Object thisObject; - - // the method to be executed String method; - - // the user object associated with the current request + ESObject current; User user; - - // arguments passed to the function - Object[] args; - - // the object path of the request we're evaluating - List requestPath; - - // the result of the + Vector args; + ESValue[] esargs; + ESValue esresult; Object result; - - // the exception thrown by the evaluator, if any. Exception exception; + protected ArrayPrototype reqPath; + private ESRequestData reqData; + + // vars for FESI EcmaScript support + protected Evaluator evaluator; + protected ObjectPrototype esNodePrototype; + protected ObjectPrototype esUserPrototype; + protected LruHashtable objectcache; + protected Hashtable prototypes; + + GlobalObject global; + HopExtension hopx; + MailExtension mailx; + FesiRpcServer xmlrpc; + ESAppNode appnode; + static String[] extensions = new String[] { + "FESI.Extensions.BasicIO", + "FESI.Extensions.FileIO", + "helma.xmlrpc.fesi.FesiRpcExtension", + "helma.framework.extensions.ImageExtension", + "helma.framework.extensions.FtpExtension", + "helma.framework.extensions.Database", + "FESI.Extensions.JavaAccess", + "FESI.Extensions.OptionalRegExp"}; // the type of request to be serviced int reqtype; @@ -57,13 +75,49 @@ public class RequestEvaluator implements Runnable { static final int XMLRPC = 2; // via XML-RPC static final int INTERNAL = 3; // generic function call, e.g. by scheduler + INode root, userroot, currentNode; /** - * Create a new RequestEvaluator for this application. + * Build a RenderContext from a RequestTrans. Checks if the path is the user home node ("user") + * or a subnode of it. Otherwise, the data root of the site is used. Two arrays are built, one + * that contains the data nodes, and anotherone with the corresponding Prototypes or Prototype.Parts. */ public RequestEvaluator (Application app) { - this.app = app; + this.app = app; + this.objectcache = new LruHashtable (100, .80f); + this.prototypes = new Hashtable (); + initEvaluator (); initialized = false; + // startThread (); + } + + + // init Script Evaluator + private void initEvaluator () { + try { + evaluator = new Evaluator(); + global = evaluator.getGlobalObject(); + for (int i=0; i 50) - throw new RuntimeException ("Path too long"); - String[] pathItems = new String [ntokens]; - for (int i=0; i 1) { + next = st.nextToken (); + if ("user".equalsIgnoreCase (next)) { + currentNode = user.getNode (); + ntokens -=1; + if (currentNode != null) + reqPath.putProperty (1, getNodeWrapper (currentNode)); + } else if ("users".equalsIgnoreCase (next)) { + currentNode = app.getUserRoot (); + ntokens -=1; + isProperty = true; + } else { + currentNode = currentNode.getSubnode (next); + ntokens -=1; + if (currentNode != null) + reqPath.putProperty (1, getNodeWrapper (currentNode)); } } + + for (int i=1; i 50) throw new RuntimeException ("Path too deep"); + } - if (currentElement == null) + if (currentNode == null) throw new FrameworkException ("Object not found."); - if (action == null) - action = getAction (currentElement, null); + Prototype p = app.getPrototype (currentNode); + next = st.nextToken (); + if (p != null) + action = p.getActionOrTemplate (next); + + if (p == null || action == null) { + currentNode = currentNode.getSubnode (next); + if (currentNode == null) + throw new FrameworkException ("Object or Action '"+next+"' not found."); + p = app.getPrototype (currentNode); + // add to reqPath array + if (currentNode != null && currentNode.getState() != INode.VIRTUAL) + reqPath.putProperty (reqPath.size(), getNodeWrapper (currentNode)); + if (p != null) + action = p.getActionOrTemplate ("main"); + } + + current = getNodeWrapper (currentNode); - if (action == null) - throw new FrameworkException ("Action not found"); } + 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, @@ -207,81 +280,27 @@ public class RequestEvaluator implements Runnable { 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); + Prototype p = app.getPrototype (root); + action = p.getActionOrTemplate (notFoundAction); if (action == null) throw new FrameworkException (notfound.getMessage ()); + current = getNodeWrapper (root); } - localrtx.timer.endEvent (txname+" init"); - ///////////////////////////////////////////////////////////////////////////// - // end of path resolution section + localrtx.timer.endEvent (requestPath+" init"); - ///////////////////////////////////////////////////////////////////////////// - // beginning of execution section try { - localrtx.timer.beginEvent (txname+" execute"); - - int actionDot = action.lastIndexOf ("."); - boolean isAction = actionDot == -1; - // set the req.action property, cutting off the _action suffix - if (isAction) - req.action = action.substring (0, action.length()-7); - else - req.action = action; - - // try calling onRequest() function on object before - // calling the actual action - try { - app.scriptingEngine.invoke (currentElement, "onRequest", new Object[0], globals, this); - } catch (RedirectException redir) { - throw redir; - } catch (Exception ignore) { - // function is not defined or caused an exception, ignore + localrtx.timer.beginEvent (requestPath+" execute"); + current.doIndirectCall (evaluator, current, action.getFunctionName (), new ESValue[0]); + if (res.mainSkin != null) { + Skin mainSkin = getSkin (null, res.mainSkin); + if (mainSkin != null) + mainSkin.render (this, null, null); } - - // do the actual action invocation - if (isAction) { - app.scriptingEngine.invoke (currentElement, action, new Object[0], globals, this); - } else { - Skin skin = app.skinmgr.getSkinInternal (app.appDir, app.getPrototype(currentElement).getName(), - action.substring (0, actionDot), action.substring (actionDot+1)); - if (skin != null) - skin.render (this, currentElement, null); - else - throw new RuntimeException ("Skin "+action+" not found in "+req.path); - } - - // check if the script set the name of a skin to render in res.skin - if (res.skin != null) { - int dot = res.skin.indexOf ("."); - Object skinObject = null; - String skinName = res.skin; - if (dot > -1) { - String soname = res.skin.substring (0, dot); - int l = requestPath.size(); - for (int i=l-1; i>=0; i--) { - Object pathelem = requestPath.get (i); - if (soname.equalsIgnoreCase (app.getPrototypeName (pathelem))) { - skinObject = pathelem; - break; - } - } - - if (skinObject == null) - throw new RuntimeException ("Skin "+res.skin+" not found in path."); - skinName = res.skin.substring (dot+1); - } - Object[] skinNameArg = new Object[1]; - skinNameArg[0] = skinName; - app.scriptingEngine.invoke (skinObject, "renderSkin", skinNameArg, globals, this); - } - - localrtx.timer.endEvent (txname+" execute"); + localrtx.timer.endEvent (requestPath+" execute"); } catch (RedirectException redirect) { - // res.redirect = redirect.getMessage (); + res.redirect = redirect.getMessage (); // if there is a message set, save it on the user object for the next request if (res.message != null) user.message = res.message; @@ -295,12 +314,12 @@ public class RequestEvaluator implements Runnable { } catch (ConcurrencyException x) { res.reset (); - if (++tries < 8) { + if (++tries < 5) { // try again after waiting some period abortTransaction (true); try { // wait a bit longer with each try - int base = 800 * tries; + int base = 500 * tries; Thread.currentThread ().sleep ((long) (base + Math.random ()*base*2)); } catch (Exception ignore) {} continue; @@ -308,8 +327,6 @@ public class RequestEvaluator implements Runnable { 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 @@ -322,7 +339,7 @@ public class RequestEvaluator implements Runnable { abortTransaction (false); - app.logEvent ("### Exception in "+app.getName()+"/"+req.path+": "+x); + IServer.getLogger().log ("### Exception in "+app.getName()+"/"+req.path+": "+x); // Dump the profiling data to System.err if (app.debug) { ((Transactor) Thread.currentThread ()).timer.dump (System.err); @@ -337,8 +354,6 @@ public class RequestEvaluator implements Runnable { res.reset (); if (error == null) { app.errorCount += 1; - // 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 (); @@ -357,31 +372,42 @@ public class RequestEvaluator implements Runnable { root = app.getDataRoot (); - HashMap globals = new HashMap (); - globals.put ("root", root); - globals.put ("res", res); - globals.put ("app", app.getAppNode()); - - currentElement = root; + global.putHiddenProperty ("root", getNodeWrapper (root)); + global.deleteProperty("user", "user".hashCode()); + global.deleteProperty ("req", "req".hashCode()); + global.putHiddenProperty ("res", ESLoader.normalizeValue(new ResponseTrans (), evaluator)); + global.deleteProperty ("path", "path".hashCode()); + global.putHiddenProperty ("app", appnode); + // convert arguments + int l = args.size (); + current = getNodeWrapper (root); if (method.indexOf (".") > -1) { StringTokenizer st = new StringTokenizer (method, "."); int cnt = st.countTokens (); for (int i=1; iError in application '"+app.getName()+"':

Request timed out.
"); @@ -532,12 +562,11 @@ public class RequestEvaluator implements Runnable { } - public synchronized Object invokeXmlRpc (String method, Object[] args) throws Exception { + public synchronized Object invokeXmlRpc (String method, Vector args) throws Exception { this.reqtype = XMLRPC; this.user = null; this.method = method; this.args = args; - this.res = new ResponseTrans (); result = null; exception = null; @@ -552,19 +581,24 @@ public class RequestEvaluator implements Runnable { return result; } - protected Object invokeDirectFunction (Object obj, String functionName, Object[] args) throws Exception { - return app.scriptingEngine.invoke (obj, functionName, args, null, this); - } - - public synchronized Object invokeFunction (Object object, String functionName, Object[] args) + public synchronized ESValue invokeFunction (INode node, String functionName, ESValue[] args) throws Exception { - reqtype = INTERNAL; - user = null; - thisObject = object; - method = functionName; - this.args =args; - this.res = new ResponseTrans (); - result = null; + ESObject obj = null; + if (node == null) + obj = global; + else + obj = getNodeWrapper (node); + return invokeFunction (obj, functionName, args); + } + + public synchronized ESValue invokeFunction (ESObject obj, String functionName, ESValue[] args) + throws Exception { + this.reqtype = INTERNAL; + this.user = null; + this.current = obj; + this.method = functionName; + this.esargs = args; + esresult = ESNull.theNull; exception = null; checkThread (); @@ -576,18 +610,17 @@ public class RequestEvaluator implements Runnable { if (exception != null) throw (exception); - return result; + return esresult; } - public synchronized Object invokeFunction (User user, String functionName, Object[] args) + public synchronized ESValue invokeFunction (User user, String functionName, ESValue[] args) throws Exception { - reqtype = INTERNAL; + this.reqtype = INTERNAL; this.user = user; - thisObject = null; - method = functionName; - this.args = args; - res = new ResponseTrans (); - result = null; + this.current = null; + this.method = functionName; + this.esargs = args; + esresult = ESNull.theNull; exception = null; checkThread (); @@ -599,7 +632,7 @@ public class RequestEvaluator implements Runnable { if (exception != null) throw (exception); - return result; + return esresult; } @@ -608,9 +641,9 @@ public class RequestEvaluator implements Runnable { * notify. */ public synchronized void stopThread () { - app.logEvent ("Stopping Thread "+rtx); + IServer.getLogger().log ("Stopping Thread "+rtx); Transactor t = rtx; - // evaluator.thread = null; + evaluator.thread = null; rtx = null; if (t != null) { if (reqtype != NONE) { @@ -632,48 +665,276 @@ public class RequestEvaluator implements Runnable { throw new ApplicationStoppedException (); if (rtx == null || !rtx.isAlive()) { - // app.logEvent ("Starting Thread"); + // IServer.getLogger().log ("Starting Thread"); rtx = new Transactor (this, app.threadgroup, app.nmgr); - // evaluator.thread = rtx; + evaluator.thread = rtx; rtx.start (); } else { notifyAll (); } } - + public Skin getSkin (ESObject thisObject, String skinname) { + INode n = null; + Prototype proto = null; + if (thisObject != null && thisObject instanceof ESNode) { + n = ((ESNode) thisObject).getNode (); + proto = app.getPrototype (n); + } else // the requested skin is global + proto = app.typemgr.getPrototype ("global"); + Skin skin = null; + if (proto != null) + skin = proto.getSkin (skinname); + // if we have a thisObject and didn't find the skin, try in hopobject + if (skin == null && n != null) { + proto = app.typemgr.getPrototype ("hopobject"); + if (proto != null) + skin = proto.getSkin (skinname); + } + return skin; + } + /** - * 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. + * Returns a node wrapper only if it already exists in the cache table. This is used + * in those places when wrappers have to be updated if they already exist. */ - public String getAction (Object obj, String action) { - if (obj == null) + public ESNode getNodeWrapperFromCache (INode n) { + if (n == null) return null; - // check if this is a public skin, i.e. something with an extension - // like "home.html" - if (action != null && action.indexOf (".") > -1) { - int dot = action.lastIndexOf ("."); - String extension = action.substring (dot+1); - String contentType = app.skinExtensions.getProperty (extension); - if (contentType != null) { - res.contentType = contentType; - return action; - } else - return null; - } else { - String act = action == null ? "main_action" : action+"_action"; - try { - if (app.scriptingEngine.hasFunction (obj, act, this)) - return act; - } catch (ScriptingException x) { - return null; - } - } - return null; + return (ESNode) objectcache.get (n); } + public ESNode getNodeWrapper (INode n) { + + if (n == null) + return null; + + ESNode esn = (ESNode) objectcache.get (n); + + if (esn == null || esn.getNode() != n) { + ObjectPrototype op = null; + String protoname = n.getPrototype (); + + // set the DbMapping of the node according to its prototype. + // 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)); + } + + try { + op = (ObjectPrototype) prototypes.get (protoname); + } catch (Exception ignore) {} + + if (op == null) + op = esNodePrototype; // no prototype found for this node. + + if ("user".equalsIgnoreCase (protoname)) + esn = new ESUser (n, this); + else + esn = new ESNode (op, evaluator, n, this); + + objectcache.put (n, esn); + // IServer.getLogger().log ("Wrapper for "+n+" created"); + } + + return esn; + } + + public ObjectPrototype getPrototype (String protoName) { + return (ObjectPrototype) prototypes.get (protoName); + } + + public void putPrototype (String protoName, ObjectPrototype op) { + prototypes.put (protoName, op); + } } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/framework/core/Skin.java b/src/helma/framework/core/Skin.java index a61263b4..bda48aed 100644 --- a/src/helma/framework/core/Skin.java +++ b/src/helma/framework/core/Skin.java @@ -1,136 +1,69 @@ // Skin.java // Copyright (c) Hannes Wallnöfer 2001 - + package helma.framework.core; -import helma.framework.*; -import helma.scripting.*; -import helma.objectmodel.INode; -import helma.objectmodel.ConcurrencyException; -import helma.util.HtmlEncoder; -import java.net.URLEncoder; -import java.io.*; import java.util.*; +import java.io.*; +import helma.framework.*; +import FESI.Data.*; +import FESI.Exceptions.*; +import helma.objectmodel.INode; +import helma.objectmodel.IServer; + /** - * This represents a Helma skin, i.e. a template created from containing Macro tags - * that will be dynamically evaluated.. It uses the request path array - * from the RequestEvaluator object to resolve Macro handlers by type name. + * This represents a HOP skin, i.e. a template created from JavaScript. It uses the request path array + * from the RequestEvaluator object to resolve dynamic tokens. */ public class Skin { - Macro[] parts; - Application app; - char[] source; - int sourceLength; - HashSet sandbox; + Object[] parts; - /** - * Create a skin without any restrictions on which macros are allowed to be called from it - */ - public Skin (String content, Application app) { - this (content, app, null); + public Skin (String content) { + parse (content); } - /** - * 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 (); - } + public void parse (String content) { - /** - * 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; - this.source = content; - this.sourceLength = length; - parse (); - } + Vector partBuffer = new Vector (); + int l = content.length (); + char cnt[] = new char[l]; + content.getChars (0, l, cnt, 0); - /** - * 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 lastIdx = 0; + for (int i = 0; i < l-1; i++) { + if (cnt[i] == '<' && cnt[i+1] == '%') { int j = i+2; - // search macr end tag - while (j < sourceLength-1 && (source[j] != '%' || source[j+1] != '>')) { + while (j < l-1 && (cnt[j] != '%' || cnt[j+1] != '>')) { j++; } if (j > i+2) { - partBuffer.add (new Macro (i, j+2)); - start = j+2; + if (i - lastIdx > 0) + partBuffer.addElement (new String (cnt, lastIdx, i - lastIdx)); + String macrotext = new String (cnt, i+2, (j-i)-2); + partBuffer.addElement (new Macro (macrotext)); + lastIdx = j+2; } i = j+1; } } + if (lastIdx < l) + partBuffer.addElement (new String (cnt, lastIdx, l - lastIdx)); - parts = new Macro[partBuffer.size()]; - partBuffer.toArray (parts); - } - - /** - * 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, HashMap paramObject) throws RedirectException { + parts = partBuffer.toArray (); + } + public void render (RequestEvaluator reval, ESNode thisNode, ESObject paramObject) { if (parts == null) - reval.res.writeCharArray (source, 0, sourceLength); - - int written = 0; + return; for (int i=0; i written) - reval.res.writeCharArray (source, written, parts[i].start-written); - parts[i].render (reval, thisObject, paramObject); - written = parts[i].end; + if (parts[i] instanceof Macro) + ((Macro) parts[i]).render (reval, thisNode, paramObject); + else + reval.res.write (parts[i]); } - if (written < sourceLength) - reval.res.writeCharArray (source, written, sourceLength-written); - } - - /** - * 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; i--) { - Object pathelem = reval.requestPath.get (i); - if (handler.equalsIgnoreCase (app.getPrototypeName (pathelem))) { - handlerObject = pathelem; + if (handler.equalsIgnoreCase (((ESNode) reval.reqPath.getProperty(i)).getPrototypeName())) { + handlerObject = (ESNode) reval.reqPath.getProperty(i); break; } } } - // the macro handler object couldn't be found - if (handlerObject == null) - objectFound = false; - } else { // this is a global macro with no handler specified - handlerObject = null; + handlerObject = reval.global; } - 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. - Object v = null; - if (app.scriptingEngine.hasFunction (handlerObject, name+"_macro", reval)) { - // System.err.println ("Getting macro from function"); - v = app.scriptingEngine.invoke (handlerObject, name+"_macro", arguments, null, reval); - } else { - // System.err.println ("Getting macro from property"); - v = app.scriptingEngine.get (handlerObject, name, reval); - } - if (v != null) - writeToResponse (v.toString (), reval.res); + if (handlerObject != null) { + ESValue v = handlerObject.doIndirectCall (reval.evaluator, handlerObject, name+"_macro", arguments); + if (v != ESUndefined.theUndefined && v != ESNull.theNull) + reval.res.write (v); } else { - String msg = "[HopMacro unhandled: "+getFullName()+"]"; + String msg = "[HopMacro unhandled: "+handler+"."+name+"]"; reval.res.write (" "+msg+" "); - app.logEvent (msg); + IServer.getLogger().log (msg); } - } catch (RedirectException redir) { - throw redir; - } catch (ConcurrencyException concur) { - throw concur; } catch (Exception x) { - x.printStackTrace(); String msg = "[HopMacro error: "+x+"]"; reval.res.write (" "+msg+" "); - app.logEvent (msg); + IServer.getLogger().log (msg); } } private void renderFromResponse (RequestEvaluator reval) { - Object value = null; - // as a transitional solution, try to get the value from the - // hardcoded fields in the response object. If not present, try - // the response object's data object. - if ("title".equals (name)) - value = reval.res.title; - else if ("head".equals (name)) - value = reval.res.head; - else if ("body".equals (name)) - value = reval.res.body; - else if ("message".equals (name)) - value = reval.res.message; - if (value == null) - value = reval.res.get (name); - if (value != null) - writeToResponse (value.toString (), reval.res); + if ("title".equals (name) && reval.res.title != null) + reval.res.write (reval.res.title); + else if ("head".equals (name) && reval.res.head != null) + reval.res.write (reval.res.head); + else if ("body".equals (name) && reval.res.body != null) + reval.res.write (reval.res.body); + else if ("message".equals (name) && reval.res.message != null) + reval.res.write (reval.res.message); } private void renderFromRequest (RequestEvaluator reval) { Object value = reval.req.get (name); if (value != null) - writeToResponse (value.toString (), reval.res); + reval.res.write (value); } - private void renderFromParam (RequestEvaluator reval, HashMap paramObject) { + private void renderFromParam (RequestEvaluator reval, ESObject paramObject) { if (paramObject == null) reval.res.write ("[HopMacro error: Skin requires a parameter object]"); else { - Object value = paramObject.get (name); - if (value != null) - writeToResponse (value.toString (), reval.res); + try { + ESValue value = paramObject.getProperty (name, name.hashCode()); + if (value != null && value != ESUndefined.theUndefined) + reval.res.write (value); + } catch (EcmaScriptException ignore) {} } } - /** - * Utility method for writing text out to the response object. - */ - void writeToResponse (String text, ResponseTrans res) { - if (text == null || text.length() == 0) - return; - String encoding = (String) parameters.get ("encoding"); - String prefix = (String) parameters.get ("prefix"); - String suffix = (String) parameters.get ("suffix"); - res.write (prefix); - res.write (encode (text, encoding)); - res.write (suffix); - } - - /** - * Utility method for performing different kind of character - * encodings on the macro output. - */ - String encode (String text, String encoding) { - if (encoding == null || text == null) - return text; - if ("html".equalsIgnoreCase (encoding)) - return HtmlEncoder.encode (text); - if ("xml".equalsIgnoreCase (encoding)) - return HtmlEncoder.encodeXml (text); - if ("form".equalsIgnoreCase (encoding)) - return HtmlEncoder.encodeFormValue (text); - if ("url".equalsIgnoreCase (encoding)) - return URLEncoder.encode (text); - return text; - } - - public String toString () { - return "[HopMacro: "+getFullName()+"]"; + return "[HopMacro: "+handler+","+name+"]"; } - - /** - * Return the full name of the macro in handler.name notation - */ - public String getFullName () { - if (fullname == null) { - if (handler == null) - fullname = name; - else - fullname = handler+"."+name; - } - return fullname; - } - } + } + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/framework/core/SkinFile.java b/src/helma/framework/core/SkinFile.java index cd00bad4..2a3f8f8a 100644 --- a/src/helma/framework/core/SkinFile.java +++ b/src/helma/framework/core/SkinFile.java @@ -5,7 +5,7 @@ package helma.framework.core; import java.util.*; import java.io.*; -import helma.util.Updatable; +import helma.objectmodel.IServer; /** @@ -13,7 +13,7 @@ import helma.util.Updatable; */ -public class SkinFile implements Updatable { +public class SkinFile { String name; Prototype prototype; @@ -30,65 +30,28 @@ public class SkinFile implements Updatable { this.skin = null; } - /** - * Create a skinfile without a file, passing the skin body directly. This is used for - * 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; - this.skin = new Skin (body, app); - } - /** - * Create a skinfile without 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.prototype = null; - this.app = app; - this.name = name; - this.file = file; - this.skin = null; - } + public void update (File f) { + this.file = f; - /** - * 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 (skin != null && lastmod != file.lastModified ()) || !file.exists (); - } + long fmod = file.lastModified (); + // we only update this if we already have read the skin + if (skin == null || lastmod == fmod) + return; - - public void update () { - - if (!file.exists ()) { - // remove skin from prototype - if (prototype != null) { - prototype.skins.remove (name); - prototype.updatables.remove (file.getName()); - } - } else { - // we only need to update if the skin has already been initialized - if (skin != null) - read (); - } + read (); } private void read () { try { FileReader reader = new FileReader (file); char c[] = new char[(int) file.length()]; - int length = reader.read (c); + reader.read (c); reader.close(); - skin = new Skin (c, length, app); + skin = new Skin (new String (c)); } catch (IOException x) { - app.logEvent ("Error reading Skin "+file+": "+x); + IServer.getLogger().log ("Error reading Skin "+file+": "+x); } lastmod = file.lastModified (); @@ -99,15 +62,45 @@ public class SkinFile implements Updatable { read (); return skin; } - - public String getName () { - return name; - } - public String toString () { - return prototype.getName()+"/"+file.getName(); - } } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/framework/core/SkinManager.java b/src/helma/framework/core/SkinManager.java deleted file mode 100644 index 45d7d4a2..00000000 --- a/src/helma/framework/core/SkinManager.java +++ /dev/null @@ -1,141 +0,0 @@ -// SkinManager.java -// Copyright (c) Hannes Wallnöfer 2002 - -package helma.framework.core; - -import java.util.Map; -import java.util.WeakHashMap; -import java.util.Iterator; -import helma.objectmodel.INode; -import java.io.*; - - -/** - * Manages skins for a Helma application - */ - - -public class SkinManager { - - Application app; - Map skincache; - - public SkinManager (Application app) { - this.app = app; - skincache = new WeakHashMap (); - } - - public Skin getSkin (Object object, String skinname, Object[] skinpath) { - Prototype proto = app.getPrototype (object); - String key = new StringBuffer(proto.getName()).append ("/").append (skinname) - .append ("#").append (skinpath.hashCode()).toString (); - // System.err.print ("SKINKEY: "+key); - Skin skin = (Skin) skincache.get (key); - if (skin != null) { - // System.err.println (" ... cached"); - return skin; - } - // System.err.println (" ... uncached"); - skin = getSkin (proto, skinname, "skin", skinpath); - if (skin != null) - skincache.put (key, skin); - return skin; - } - - - protected Skin getSkin (Prototype proto, String skinname, String extension, 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 -1) { @@ -115,7 +105,7 @@ public class Template extends ActionFile { templateBody.append ((char) c); c = lineReader.read (); } - } catch (IOException srx) {} + } nextLine = st.hasMoreTokens () ? st.nextToken () : null; @@ -130,7 +120,7 @@ public class Template extends ActionFile { // 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 0) { - try { - FunctionFile ff = new FunctionFile (tmpfile, tmpname, proto); - updatables.put (list[i], ff); - nfunc.put (tmpname, ff); - } catch (Throwable x) { - app.logEvent ("Error creating prototype: "+x); - } - - } else if (list[i].endsWith (app.actionExtension) && tmpfile.length () > 0) { - try { - ActionFile af = new ActionFile (tmpfile, tmpname, proto); - updatables.put (list[i], af); - nact.put (tmpname, af); - } catch (Throwable x) { - app.logEvent ("Error creating prototype: "+x); - } - } else if (list[i].endsWith (app.skinExtension)) { - try { - SkinFile sf = new SkinFile (tmpfile, tmpname, proto); - updatables.put (list[i], sf); - nskins.put (tmpname, sf); - } catch (Throwable x) { - app.logEvent ("Error creating prototype: "+x); - } - } - } - } - - // Create and register type properties file - File propfile = new File (dir, "type.properties"); - SystemProperties props = new SystemProperties (propfile.getAbsolutePath ()); - DbMapping dbmap = new DbMapping (app, name, props); - updatables.put ("type.properties", dbmap); - - - proto.templates = ntemp; - proto.functions = nfunc; - proto.actions = nact; - proto.skins = nskins; - proto.updatables = updatables; - - // init prototype on evaluators that are already initialized. - /* Iterator evals = getRegisteredRequestEvaluators (); - while (evals.hasNext ()) { - RequestEvaluator reval = (RequestEvaluator) evals.next (); - proto.initRequestEvaluator (reval); - }*/ - app.scriptingEngine.updatePrototype (proto); - - } - - - /** - * Update a prototype based on the directory which defines it. - */ - public void updatePrototype (String name, File dir, Prototype proto) { - - boolean needsUpdate = false; - HashSet updatables = null; - - // our plan is to do as little as possible, so first check if anything has changed at all... - for (Iterator i = proto.updatables.values().iterator(); i.hasNext(); ) { - Updatable upd = (Updatable) i.next(); - if (upd.needsUpdate ()) { - if (updatables == null) - updatables = new HashSet (); - needsUpdate = true; - updatables.add (upd); - } - } - - // check if file have been created since last update - if (proto.lastUpdate < dir.lastModified ()) { - String[] list = dir.list(); - for (int i=0; i 0) { try { FunctionFile ff = new FunctionFile (tmpfile, tmpname, proto); - proto.updatables.put (list[i], ff); - proto.functions.put (tmpname, ff); + nfunc.put (tmpname, ff); } catch (Throwable x) { - app.logEvent ("Error updating prototype: "+x); + IServer.getLogger().log ("Error creating prototype: "+x); } - } else if (list[i].endsWith (app.actionExtension)) { + } else if (list[i].endsWith (app.actionExtension) && tmpfile.length () > 0) { try { - ActionFile af = new ActionFile (tmpfile, tmpname, proto); - proto.updatables.put (list[i], af); - proto.actions.put (tmpname, af); + Action af = new Action (tmpfile, tmpname, proto); + nact.put (tmpname, af); } catch (Throwable x) { - app.logEvent ("Error updating prototype: "+x); + IServer.getLogger().log ("Error creating prototype: "+x); } + } else if (list[i].endsWith (app.skinExtension)) { + try { + SkinFile sf = new SkinFile (tmpfile, tmpname, proto); + nskins.put (tmpname, sf); + } catch (Throwable x) { + IServer.getLogger().log ("Error creating prototype: "+x); + } + } + } + proto.templates = ntemp; + proto.functions = nfunc; + proto.actions = nact; + proto.skins = nskins; - } else if (list[i].endsWith (app.skinExtension)) { - SkinFile sf = new SkinFile (tmpfile, tmpname, proto); - proto.updatables.put (list[i], sf); - proto.skins.put (tmpname, sf); - } + // init prototype on evaluators that are already initialized. + Iterator evals = getRegisteredRequestEvaluators (); + while (evals.hasNext ()) { + RequestEvaluator reval = (RequestEvaluator) evals.next (); + proto.initRequestEvaluator (reval); } - // next go through existing updatables - if (updatables != null) { - for (Iterator i = updatables.iterator(); i.hasNext(); ) { - Updatable upd = (Updatable) i.next(); - - if (upd.needsUpdate ()) { - if (upd instanceof DbMapping) - rewire = true; - try { - upd.update (); - } catch (Exception x) { - if (upd instanceof DbMapping) - app.logEvent ("Error updating db mapping for type "+name+": "+x); - else - app.logEvent ("Error updating "+upd+" of prototye type "+name+": "+x); - } - } - } - } - app.scriptingEngine.updatePrototype (proto); } - /*public void initRequestEvaluator (RequestEvaluator reval) { + public void updatePrototype (String name, File dir, Prototype proto) { + // IServer.getLogger().log ("updating prototype "+name); + + String list[] = dir.list(); + Hashtable ntemp = new Hashtable (); + Hashtable nfunc = new Hashtable (); + Hashtable nact = new Hashtable (); + Hashtable nskins = new Hashtable (); + + for (int i=0; i 0) { + FunctionFile ff = proto.getFunctionFile (tmpname); + try { + if (ff == null) { + ff = new FunctionFile (tmpfile, tmpname, proto); + idleSeconds = 0; + } else if (ff.lastmod != tmpfile.lastModified ()) { + ff.update (tmpfile); + idleSeconds = 0; + } + } catch (Throwable x) { + IServer.getLogger().log ("Error updating prototype: "+x); + } + nfunc.put (tmpname, ff); + + } else if (list[i].endsWith (app.actionExtension) && tmpfile.length () > 0) { + Action af = proto.getAction (tmpname); + try { + if (af == null) { + af = new Action (tmpfile, tmpname, proto); + idleSeconds = 0; + } else if (af.lastmod != tmpfile.lastModified ()) { + af.update (tmpfile); + idleSeconds = 0; + } + } catch (Throwable x) { + IServer.getLogger().log ("Error updating prototype: "+x); + } + nact.put (tmpname, af); + + } else if (list[i].endsWith (app.skinExtension)) { + SkinFile sf = proto.getSkinFile (tmpname); + try { + if (sf == null) { + sf = new SkinFile (tmpfile, tmpname, proto); + idleSeconds = 0; + } else if (sf.lastmod != tmpfile.lastModified ()) { + sf.update (tmpfile); + idleSeconds = 0; + } + } catch (Throwable x) { + IServer.getLogger().log ("Error updating prototype: "+x); + } + nskins.put (tmpname, sf); + + } else if ("type.properties".equalsIgnoreCase (list[i])) { + try { + if (proto.dbmap.read ()) { + idleSeconds = 0; + rewire = true; + } + } catch (Exception ignore) { + IServer.getLogger().log ("Error updating db mapping for type "+name+": "+ignore); + } + } + } + proto.templates = ntemp; + proto.functions = nfunc; + proto.actions = nact; + proto.skins = nskins; + } + + + + public void initRequestEvaluator (RequestEvaluator reval) { if (!registeredEvaluators.contains (reval)) registeredEvaluators.add (reval); - for (Iterator it = prototypes.values().iterator(); it.hasNext(); ) { - Prototype p = (Prototype) it.next (); + for (Enumeration en = prototypes.elements(); en.hasMoreElements(); ) { + Prototype p = (Prototype) en.nextElement (); p.initRequestEvaluator (reval); } reval.initialized = true; @@ -405,7 +331,103 @@ public class TypeManager { public int countRegisteredRequestEvaluators () { return registeredEvaluators.size (); - } */ + } } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/framework/core/User.java b/src/helma/framework/core/User.java index 7ce3e1e9..839c372f 100644 --- a/src/helma/framework/core/User.java +++ b/src/helma/framework/core/User.java @@ -7,46 +7,29 @@ import java.io.*; import java.util.*; import java.net.URLEncoder; import helma.objectmodel.*; -import helma.objectmodel.db.*; /** - * This represents a user who is currently using the Hop application. This does - * not just comprend registered users, but anybody who happens to surf the site. - * Depending on whether the user is logged in or not, the user object holds a - * persistent user node or just a transient cache node + * This represents a user who is currently using the HOP application. This does + * not just comprend registered users, but anybody who happens to surf the site. */ public class User implements Serializable { Application app; String sessionID; - - // the unique id (login name) for the user, if logged in - String uid; - - // the handle to this user's persistent db node, if logged in - NodeHandle nhandle; - - // the transient cache node. This stays the same across logins and logouts. - // If logged out, this also represents the user's main node. - TransientNode cache; - - DbMapping umap; + String uid, nid; long onSince, lastTouched; - - // used to remember messages to the user between requests - - // used for redirects. + Node cache; + DbMapping umap; String message; public User (String sid, Application app) { this.uid = null; - this.nhandle = null; - this.app = app; - setNode (null); - umap = app.getDbMapping ("user"); - cache = new TransientNode ("[session cache]"); - cache.setPrototype ("user"); - cache.setDbMapping (umap); + this.nid = null; + this.app = app; + setNode (null); + cache = new Node (sid); + cache.setPrototype ("user"); sessionID = sid; onSince = System.currentTimeMillis (); lastTouched = onSince; @@ -54,38 +37,26 @@ public class User implements Serializable { /** - * This is used to turn for login and logout. - * Calling this weith a DB Node object will turn an anonymous user into a registered or known one. - * The user object remains the same, but he or she gets some persistent storage. - * On the other side, calling this method with a parameter value of null is means the user - * is logged out and will be represented by its transient cache node. + * This is used to turn an anonymous user into a registered or known one. + * The user object remains the same, but she gets some persistent storage. */ public void setNode (INode n) { // IServer.getLogger().log ("esn = "+esn); if (n == null) { - nhandle = null; + nid = null; uid = null; } else { - uid = n.getElementName (); - nhandle = ((Node) n).getHandle (); + uid = n.getNameOrID (); + nid = n.getID (); + umap = n.getDbMapping (); } - // System.err.println ("User.setNode: "+nhandle); } public INode getNode () { - if (nhandle == null) { + if (uid == null) { return cache; } else { - // in some special cases, a user's node handle may go bad, for instance - // if something bad happens during registration. For this reason, we check - // if the handle actually works. If not, it is reset to the transient cache, which - // means the user is logged out. - Node n = nhandle.getNode (app.nmgr.safe); - if (n == null) { - setNode (null); - return cache; - } - return n; + return app.nmgr.safe.getNode (nid, umap); } } @@ -98,37 +69,39 @@ public class User implements Serializable { lastTouched = System.currentTimeMillis (); } - public long lastTouched () { + public long touched () { return lastTouched; } - - public long onSince () { - return onSince; - } - - /** - * 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; - } - - /** - * Return the transient cache node for this user. - */ - public INode getCache () { - return cache; - } - - /** - * Reset the session cache node, clearing all properties. - * This is done by recreating the cache node object. - */ - public void clearCache () { - cache = new TransientNode ("[session cache]"); - cache.setPrototype ("user"); - cache.setDbMapping (umap); - } + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/framework/core/ZippedAppFile.java b/src/helma/framework/core/ZippedAppFile.java deleted file mode 100644 index f3032629..00000000 --- a/src/helma/framework/core/ZippedAppFile.java +++ /dev/null @@ -1,186 +0,0 @@ -// ZippedFile.java -// Copyright (c) Hannes Wallnöfer 2001 - -package helma.framework.core; - -import java.util.*; -import java.util.zip.*; -import java.io.*; -import helma.framework.*; -import helma.scripting.*; -import helma.util.Updatable; -import helma.util.SystemProperties; -import helma.objectmodel.db.DbMapping; - -/** - * This represents a Zip-File which may contain other Updatables for one or more prototypes. - */ - - -public class ZippedAppFile implements Updatable { - - Application app; - File file; - long lastmod; - - - public ZippedAppFile (File file, Application app) { - this.app = app; - this.file = file; - // System.err.println ("CREATING ZIP FILE "+this); - } - - - /** - * 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 void update () { - - if (!file.exists ()) { - remove (); - - } else { - - ZipFile zip = null; - // collect created protos - we need this to check DbMappings for each created - // prototype afterwards - HashSet newPrototypes = new HashSet (); - try { - lastmod = file.lastModified (); - // System.err.println ("UPDATING ZIP FILE "+this); - zip = new ZipFile (file); - for (Enumeration en = zip.entries (); en.hasMoreElements (); ) { - ZipEntry entry = (ZipEntry) en.nextElement (); - String ename = entry.getName (); - StringTokenizer st = new StringTokenizer (ename, "/"); - if (st.countTokens () == 2) { - String dir = st.nextToken (); - String fname = st.nextToken (); - // System.err.println ("ZIPENTRY: "+ dir +" ~ "+fname); - Prototype proto = app.typemgr.getPrototype (dir); - if (proto == null) { - proto = app.typemgr.createPrototype (dir); - newPrototypes.add (proto); - } - if (fname.endsWith (".hac")) { - String name = fname.substring (0, fname.lastIndexOf (".")); - String content = getZipEntryContent (zip, entry); - // System.err.println ("["+content+"]"); - ActionFile act = new ActionFile (content, name, proto); - proto.actions.put (name, act); - } - else if (fname.endsWith (".hsp")) { - String name = fname.substring (0, fname.lastIndexOf (".")); - String content = getZipEntryContent (zip, entry); - // System.err.println ("["+content+"]"); - Template tmp = new Template (content, name, proto); - proto.templates.put (name, tmp); - } - else if (fname.endsWith (".skin")) { - String name = fname.substring (0, fname.lastIndexOf (".")); - String content = getZipEntryContent (zip, entry); - // System.err.println ("["+content+"]"); - SkinFile skin = new SkinFile (content, name, proto); - proto.skins.put (name, skin); - } - else if (fname.endsWith (".js")) { - String name = fname.substring (0, fname.lastIndexOf (".")); - String content = getZipEntryContent (zip, entry); - // System.err.println ("["+content+"]"); - FunctionFile ff = new FunctionFile (content, name, proto); - proto.functions.put (name, ff); - } - else if ("type.properties".equalsIgnoreCase (fname)) { - String name = fname.substring (0, fname.lastIndexOf (".")); - SystemProperties props = new SystemProperties (zip.getInputStream (entry)); - // DbMapping does its own registering, just construct it. - new DbMapping (app, proto.getName (), props); - } - } - } - for (Iterator it = newPrototypes.iterator (); it.hasNext (); ) { - Prototype proto = (Prototype) it.next (); - if (app.getDbMapping (proto.getName ()) == null) { - // DbMapping doesn't exist, we still need to create one - SystemProperties props = new SystemProperties (); - // DbMapping does its own registering, just construct it. - new DbMapping (app, proto.getName (), props); - } - } - } catch (Throwable x) { - System.err.println ("Error updating ZipFile: "+x); - } finally { - try { - zip.close (); - } catch (Exception ignore) {} - } - } - - } - - void remove () { - app.typemgr.zipfiles.remove (file.getName()); - // System.err.println ("REMOVING ZIP FILE "+this); - } - - - public String getZipEntryContent (ZipFile zip, ZipEntry entry) throws IOException { - int size = (int) entry.getSize (); - char[] c = new char[size]; - InputStreamReader reader = new InputStreamReader (zip.getInputStream (entry)); - reader.read (c); - return new String (c); - } - - - public String toString () { - return file.getName(); - } - - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/framework/demo/SimplePathElement.java b/src/helma/framework/demo/SimplePathElement.java deleted file mode 100644 index b18b8286..00000000 --- a/src/helma/framework/demo/SimplePathElement.java +++ /dev/null @@ -1,74 +0,0 @@ -// SimplePathElement.java -// Copyright Hannes Wallnöfer 2001 - -package helma.framework.demo; - -import helma.framework.IPathElement; - -/** - * This is an example implementation for the helma.framework.IPathElement interface. - * It creates any child element which is requested on the fly without ever asking. - */ - -public class SimplePathElement implements IPathElement { - - String name; - String prototype; - IPathElement parent; - - /** - * Constructor for the root element. - */ - public SimplePathElement () { - name = "root"; - prototype = "root"; - parent = null; - } - - /** - * Constructor for non-root elements. - */ - public SimplePathElement (String n, IPathElement p) { - name = n; - prototype = "hopobject"; - parent = p; - } - - /** - * Returns a child element for this object, creating it on the fly. - */ - public IPathElement getChildElement (String n) { - return new SimplePathElement (n, this); - } - - /** - * Returns this object's parent element - */ - public IPathElement getParentElement () { - return parent; - } - - /** - * Returns the element name to be used for this object. - */ - public String getElementName () { - return name; - } - - /** - * Returns the name of the scripting prototype to be used for this object. - * This will be "root" for the root element and "hopobject for everything else. - */ - public String getPrototype () { - return prototype; - } - - /** - * Returns a string representation of this element. - */ - public String toString () { - return "SimplePathElement "+name; - } - -} - diff --git a/src/helma/scripting/fesi/extensions/Database.java b/src/helma/framework/extensions/Database.java similarity index 98% rename from src/helma/scripting/fesi/extensions/Database.java rename to src/helma/framework/extensions/Database.java index b30f2077..845a5046 100644 --- a/src/helma/scripting/fesi/extensions/Database.java +++ b/src/helma/framework/extensions/Database.java @@ -16,12 +16,11 @@ // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// Modified to use Helma database connections, Hannes Wallnöfer 2000 +// Modified to use HOP database connections, Hannes Wallnöfer 2000 -package helma.scripting.fesi.extensions; +package helma.framework.extensions; -import helma.framework.core.Application; -import helma.objectmodel.db.DbSource; +import helma.objectmodel.*; import FESI.Parser.*; import FESI.AST.*; import FESI.Interpreter.*; @@ -60,12 +59,13 @@ class ESDatabase extends ESObject { ESDatabase(ESObject prototype, Evaluator evaluator, ESObject esRowSetPrototype, - DbSource dbsource, int flag) { + String dbsource, int flag) { super(prototype, evaluator); this.esRowSetPrototype = esRowSetPrototype; // specific to an evaluator try { - connection = dbsource.getConnection (); - driverName = dbsource.getDriverName (); + DbSource src = (DbSource) IServer.dbSources.get (dbsource.toLowerCase ()); + connection = src.getConnection (); + this.driverName = src.getDriverName (); } catch (Exception e) { // System.err.println("##Cannot find driver class: " + e); // e.printStackTrace(); @@ -717,17 +717,11 @@ public class Database extends Extension { private transient Evaluator evaluator = null; private ESObject esDatabasePrototype = null; private ESObject esRowSetPrototype = null; - Application app; public Database () { super(); } - - - public void setApplication (Application app) { - this.app = app; - } - + ////////////////// Added by Hannes Wallnoefer class GlobalGetDBConnection extends BuiltinFunctionObject { GlobalGetDBConnection(String name, Evaluator evaluator, FunctionPrototype fp) { @@ -737,17 +731,13 @@ public class Database extends Extension { throws EcmaScriptException { if (arguments.length != 1) throw new EcmaScriptException ("Wrong number of arguments in getDBConnection(dbsource)"); - String srcname = arguments[0].toString (); - DbSource dbsrc = app.getDbSource (srcname.toLowerCase ()); - if (dbsrc == null) - throw new EcmaScriptException ("DbSource "+srcname+" does not exist"); ESDatabase db = new ESDatabase (esDatabasePrototype, this.evaluator, - esRowSetPrototype, dbsrc, 0); + esRowSetPrototype, arguments[0].toString(), 0); return db; } } - + class GlobalObjectDatabase extends BuiltinFunctionObject { GlobalObjectDatabase(String name, Evaluator evaluator, FunctionPrototype fp) { super(fp, evaluator, name, 1); diff --git a/src/helma/scripting/fesi/extensions/ESMail.java b/src/helma/framework/extensions/ESMail.java similarity index 80% rename from src/helma/scripting/fesi/extensions/ESMail.java rename to src/helma/framework/extensions/ESMail.java index 5bd5883a..3db40051 100644 --- a/src/helma/scripting/fesi/extensions/ESMail.java +++ b/src/helma/framework/extensions/ESMail.java @@ -2,7 +2,7 @@ // Copyright (c) Hannes Wallnöfer 1998-2000 -package helma.scripting.fesi.extensions; +package helma.framework.extensions; import javax.mail.*; import javax.mail.internet.*; @@ -10,18 +10,19 @@ import javax.activation.*; import java.io.*; import java.util.*; import helma.framework.core.*; -import helma.util.*; +import helma.objectmodel.*; import FESI.Data.*; import FESI.Interpreter.*; import FESI.Exceptions.*; /** * A JavaScript wrapper around a JavaMail message class to send - * mail via SMTP from Helma + * mail via SMTP from HOP */ public class ESMail extends ESObject implements Serializable { + INode node; MailExtension mailx; Properties mprops; MimeMessage message; @@ -56,7 +57,7 @@ public class ESMail extends ESObject implements Serializable { Session session = Session.getDefaultInstance(props, null); message = new MimeMessage (session); } catch (Throwable t) { - this.evaluator.reval.app.logEvent ("Error in mail constructor: "+t); + IServer.getLogger().log ("caught in mail constructor: "+t); } } @@ -93,16 +94,20 @@ public class ESMail extends ESObject implements Serializable { multipart = new MimeMultipart (); } for (int i=0; i "+dbField); + } + } catch (Exception x) { + IServer.getLogger ().log ("Error in type.properties: "+x.getMessage ()); + } + } + + prop2db = p2d; + db2prop = d2p; + + String ano = props.getProperty ("_anonymous"); + if (ano != null) { + // comma-separated list of true/false values + StringTokenizer st = new StringTokenizer (ano, ",; "); + anonymous = new Boolean[st.countTokens()]; + for (int i=0; i=0; i--) { + path[i] = p; + p = p.getParent (); + } + return path; + } public void setName (String name) { - // if (name.indexOf('/') > -1) - // throw new RuntimeException ("The name of the node must not contain \"/\"."); + 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 @@ -195,12 +245,15 @@ public class TransientNode implements INode, Serializable { nodeMap.put (elem.getID (), elem); nodes.insertElementAt (elem, where); - if (elem instanceof TransientNode) { - TransientNode node = (TransientNode) elem; + if (elem instanceof Node) { + Node node = (Node) elem; if (node.parent == null) { node.parent = this; node.anonymous = true; } + if (node.parent != null && (node.parent != this || !node.anonymous)) { + node.registerLink (this); + } } lastmodified = System.currentTimeMillis (); @@ -224,7 +277,7 @@ public class TransientNode implements INode, Serializable { boolean anon = false; if (nm == null || "".equals (nm.trim ())) anon = true; - INode n = new TransientNode (nm); + INode n = new Node (nm); if (anon) addNode (n, where); else @@ -236,19 +289,11 @@ public class TransientNode implements INode, Serializable { /** * register a node that links to this node. */ - /* protected void registerLink (TransientNode from) { + protected void registerLink (Node 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, false); } public INode getSubnode (String name) { @@ -257,18 +302,18 @@ public class TransientNode implements INode, Serializable { public INode getSubnode (String name, boolean inherit) { StringTokenizer st = new StringTokenizer (name, "/"); - TransientNode retval = this, runner; + Node 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); + retval = runner.nodeMap == null ? null : (Node) runner.nodeMap.get (next); if (retval == null) - retval = (TransientNode) runner.getNode (next, inherit); + retval = (Node) runner.getNode (next, inherit); if (retval == null && inherit && runner == this && parent != null) - retval = (TransientNode) parent.getSubnode (next, inherit); + retval = (Node) parent.getSubnode (next, inherit); } return retval; } @@ -296,11 +341,11 @@ public class TransientNode implements INode, Serializable { public void removeNode (INode node) { // IServer.getLogger().log ("removing: "+ node); releaseNode (node); - TransientNode n = (TransientNode) node; + Node n = (Node) 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); + Node link = (Node) n.links.elementAt (i); link.releaseNode (n); } if (n.proplinks != null) { @@ -310,12 +355,12 @@ public class TransientNode implements INode, Serializable { p.node.propMap.remove (p.propname.toLowerCase ()); } catch (Exception ignore) {} } - /* for (Enumeration e2 = n.properties (); e2.hasMoreElements (); ) { + for (Enumeration e2 = n.properties (); e2.hasMoreElements (); ) { // tell all nodes that are properties of n that they are no longer used as such Property p = (Property) n.get ((String) e2.nextElement (), false); if (p != null && p.type == Property.NODE) p.unregisterNode (); - } */ + } // 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 (); ) { @@ -323,7 +368,7 @@ public class TransientNode implements INode, Serializable { } int m = v.size (); for (int i=0; i -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; + } + } + + public String getText () { + if (content != null) { + if (getContentType ().indexOf ("text/") == 0) { + return new String (content); + } else { + return null; + } + } + return null; + } + + + public String getHref (INode root, INode userroot, String tmpname, String prefix) { + return prefix + getUrl (root, userroot, tmpname); + } + + public String getUrl (INode root, INode userroot, String tmpname) { throw new RuntimeException ("HREFs on transient (non-db based) Nodes not supported"); - } */ + } public long lastModified () { @@ -583,29 +750,26 @@ public class TransientNode implements INode, Serializable { } public String toString () { - return "TransientNode " + name; + return "Node " + name; } + protected Node convert (INode n) { + Hashtable ntable = new Hashtable (); + Node converted = new Node (n, ntable, false); + return converted; + } + INode cacheNode; /** - * Get the cache node for this node. This can - * be used to store transient cache data per node - * from Javascript. + * 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(); + cacheNode = new Node(); return cacheNode; } - /** - * Reset the cache node for this node. - */ - public synchronized void clearCacheNode () { - cacheNode = null; - } - } diff --git a/src/helma/objectmodel/NodeDataSource.java b/src/helma/objectmodel/NodeDataSource.java new file mode 100644 index 00000000..47aca47e --- /dev/null +++ b/src/helma/objectmodel/NodeDataSource.java @@ -0,0 +1,46 @@ +// NodeDataSource.java +// Copyright (c) Hannes Wallnöfer 1999-2000 + +package helma.objectmodel; + +import javax.activation.*; +import java.io.*; + +/** + * Makes Nodes usable as Datasources in the Java Activation Framework (JAF) + */ + +public class NodeDataSource implements DataSource { + + private INode node; + private String name; + + public NodeDataSource (INode node) { + this.node = node; + this.name = node.getName (); + } + + public NodeDataSource (INode node, String name) { + this.node = node; + this.name = name; + } + + + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(node.getContent ()); + } + + public OutputStream getOutputStream () throws IOException { + throw new IOException ("Can't write to Node object."); + } + + public String getContentType() { + return node.getContentType (); + } + + public String getName() + { + return name; + } + +} diff --git a/src/helma/objectmodel/Property.java b/src/helma/objectmodel/Property.java index 4592e903..71a0f7c2 100644 --- a/src/helma/objectmodel/Property.java +++ b/src/helma/objectmodel/Property.java @@ -4,10 +4,7 @@ package helma.objectmodel; import helma.util.*; -import java.util.Vector; -import java.util.Hashtable; -import java.util.Date; -import java.util.Enumeration; +import java.util.*; import java.io.*; import java.text.*; @@ -15,11 +12,11 @@ import java.text.*; * A property implementation for Nodes stored inside a database. */ -public final class Property implements IProperty, Serializable { +public final class Property implements IProperty, Serializable, Cloneable { protected String propname; - protected TransientNode node; + protected Node node; public String svalue; public boolean bvalue; @@ -31,11 +28,11 @@ public final class Property implements IProperty, Serializable { public int type; - public Property (TransientNode node) { + public Property (Node node) { this.node = node; } - public Property (String propname, TransientNode node) { + public Property (String propname, Node node) { this.propname = propname; this.node = node; } @@ -66,16 +63,39 @@ public final class Property implements IProperty, Serializable { public void setStringValue (String value) throws ParseException { if (type == NODE) - this.nvalue = null; + unregisterNode (); + // IServer.getLogger().log ("setting string value of property "+propname + " to "+value); + if (type == DATE) { + SimpleDateFormat dateformat = new SimpleDateFormat (); + dateformat.setLenient (true); + Date date = dateformat.parse (value); + this.lvalue = date.getTime (); + return; + } + if (type == BOOLEAN) { + if ("true".equalsIgnoreCase (value)) + this.bvalue = true; + else if ("false".equalsIgnoreCase (value)) + this.bvalue = false; + return; + } + if (type == INTEGER) { + this.lvalue = Long.parseLong (value); + return; + } + if (type == FLOAT) { + this.dvalue = new Double (value).doubleValue (); + return; + } if (type == JAVAOBJECT) this.jvalue = null; - type = STRING; this.svalue = value; + type = STRING; } public void setIntegerValue (long value) { if (type == NODE) - this.nvalue = null; + unregisterNode (); if (type == JAVAOBJECT) this.jvalue = null; type = INTEGER; @@ -84,7 +104,7 @@ public final class Property implements IProperty, Serializable { public void setFloatValue (double value) { if (type == NODE) - this.nvalue = null; + unregisterNode (); if (type == JAVAOBJECT) this.jvalue = null; type = FLOAT; @@ -93,7 +113,7 @@ public final class Property implements IProperty, Serializable { public void setDateValue (Date value) { if (type == NODE) - this.nvalue = null; + unregisterNode (); if (type == JAVAOBJECT) this.jvalue = null; type = DATE; @@ -102,7 +122,7 @@ public final class Property implements IProperty, Serializable { public void setBooleanValue (boolean value) { if (type == NODE) - this.nvalue = null; + unregisterNode (); if (type == JAVAOBJECT) this.jvalue = null; type = BOOLEAN; @@ -112,18 +132,58 @@ public final class Property implements IProperty, Serializable { public void setNodeValue (INode value) { if (type == JAVAOBJECT) this.jvalue = null; + if (type == NODE && nvalue != value) { + unregisterNode (); + registerNode (value); + } else if (type != NODE) + registerNode (value); type = NODE; this.nvalue = value; } public void setJavaObjectValue (Object value) { if (type == NODE) - this.nvalue = null; + unregisterNode (); type = JAVAOBJECT; this.jvalue = value; } + /** + * tell a the value node that it is no longer used as a property. + */ + protected void unregisterNode () { + if (nvalue != null && nvalue instanceof Node) { + Node n = (Node) nvalue; + if (!n.anonymous && propname.equals (n.getName()) && this.node == n.getParent()) { + // this is the "main" property of a named node, so handle this as a total delete. + IServer.getLogger().log ("deleting named property"); + if (n.proplinks != null) { + for (Enumeration e = n.proplinks.elements (); e.hasMoreElements (); ) { + Property p = (Property) e.nextElement (); + p.node.propMap.remove (p.propname.toLowerCase ()); + } + } + if (n.links != null) { + for (Enumeration e = n.links.elements (); e.hasMoreElements (); ) { + Node n2 = (Node) e.nextElement (); + n2.releaseNode (n); + } + } + + } else { + n.unregisterPropLink (this); + } + } + } + + /** + * tell the value node that it is being used as a property value. + */ + protected void registerNode (INode n) { + if (n != null && n instanceof Node) + ((Node) n).registerPropLink (this); + } public String getStringValue () { switch (type) { @@ -141,7 +201,7 @@ public final class Property implements IProperty, Serializable { case NODE: return nvalue.getName (); case JAVAOBJECT: - return jvalue == null ? null : jvalue.toString (); + return jvalue.toString (); } return ""; } @@ -187,9 +247,86 @@ public final class Property implements IProperty, Serializable { return null; } + public String getEditor () { + switch (type) { + case STRING: + return "password".equalsIgnoreCase (propname) ? + "" : + "" ; + case BOOLEAN: + return ""; + case INTEGER: + return "" ; + case FLOAT: + return "" ; + case DATE: + SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm"); + String date = format.format (new Date (lvalue)); + return ""; + case NODE: + return ""; + } + return ""; + } + + private String escape (String s) { + char c[] = new char[s.length()]; + s.getChars (0, c.length, c, 0); + StringBuffer b = new StringBuffer (); + int copyfrom = 0; + for (int i = 0; i < c.length; i++) { + switch (c[i]) { + case '\\': + case '"': + if (i-copyfrom > 0) + b.append (c, copyfrom, i-copyfrom); + b.append ('\\'); + b.append (c[i]); + copyfrom = i+1; + } + } + if (c.length-copyfrom > 0) + b.append (c, copyfrom, c.length-copyfrom); + return b.toString (); + } public int getType () { return type; } + public String getTypeString () { + switch (type) { + case STRING: + return "string"; + case BOOLEAN: + return "boolean"; + case DATE: + return "date"; + case INTEGER: + return "integer"; + case FLOAT: + return "float"; + case NODE: + return "node"; + } + return ""; + } + + + public Object clone () { + try { + Property c = (Property) super.clone(); + c.propname = this.propname; + c.svalue = this.svalue; + c.bvalue = this.bvalue; + c.lvalue = this.lvalue; + c.dvalue = this.dvalue; + c.type = this.type; + return c; + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError (); + } + } + } diff --git a/src/helma/objectmodel/Relation.java b/src/helma/objectmodel/Relation.java new file mode 100644 index 00000000..2228ae94 --- /dev/null +++ b/src/helma/objectmodel/Relation.java @@ -0,0 +1,341 @@ +// Relation.java +// Copyright (c) Hannes Wallnöfer 1997-2000 + +package helma.objectmodel; + +import helma.framework.core.Application; +import java.util.Properties; + +/** + * This describes how a property of a persistent Object is stored in a + * relational database table. This can be either a scalar property (string, date, number etc.) + * or a reference to one or more other objects. + */ +public class Relation { + + // TODO: explain hop mapping types + public final static int INVALID = -1; + public final static int PRIMITIVE = 0; + public final static int FORWARD = 1; + public final static int BACKWARD = 2; + public final static int DIRECT = 3; + + public DbMapping home; + public DbMapping other; + public String propname; + protected String localField, remoteField; + public int direction; + + public boolean virtual; + public boolean readonly; + public boolean aggressiveLoading; + public boolean aggressiveCaching; + public boolean subnodesAreProperties; + public String order; + public String groupbyorder; + public String groupby; + public String prototype; + + Relation filter = null; // additional relation used to filter subnodes + + /** + * This constructor is used to directly construct a Relation, as opposed to reading it from a proerty file + */ + public Relation (DbMapping other, String localField, String remoteField, int direction, boolean subnodesAreProperties) { + this.other = other; + this.localField = localField; + this.remoteField = remoteField; + this.direction = direction; + this.subnodesAreProperties = subnodesAreProperties; + } + + /** + * Reads a relation entry from a line in a properties file. + */ + public Relation (String desc, String propname, DbMapping home, Properties props) { + + this.home = home; + this.propname = propname; + other = null; + Application app = home.getApplication (); + boolean mountpoint = false; + + if (desc == null || "".equals (desc.trim ())) { + if (propname != null) { + direction = PRIMITIVE; + localField = propname; + } else { + direction = INVALID; + localField = propname; + } + } else { + desc = desc.trim (); + String descLower = desc.toLowerCase (); + if (descLower.startsWith ("[virtual]")) { + desc = desc.substring (9).trim (); + virtual = true; + } else if (descLower.startsWith ("[collection]")) { + desc = desc.substring (12).trim (); + virtual = true; + } else if (descLower.startsWith ("[mountpoint]")) { + desc = desc.substring (12).trim (); + virtual = true; + mountpoint = true; + } else { + virtual = false; + } + if (descLower.startsWith ("[readonly]")) { + desc = desc.substring (10).trim (); + readonly = true; + } else { + readonly = false; + } + if (desc.indexOf ("<") > -1) { + direction = BACKWARD; + int lt = desc.indexOf ("<"); + int dot = desc.indexOf ("."); + String otherType = dot < 0 ? desc.substring (1).trim () : desc.substring (1, dot).trim (); + other = app.getDbMapping (otherType); + if (other == null) + throw new RuntimeException ("DbMapping for "+otherType+" not found from "+home.typename); + remoteField = dot < 0 ? null : desc.substring (dot+1).trim (); + localField = lt < 0 ? null : desc.substring (0, lt).trim (); + if (mountpoint) prototype = otherType; + } else if (desc.indexOf (">") > -1) { + direction = FORWARD; + int bt = desc.indexOf (">"); + int dot = desc.indexOf ("."); + String otherType = dot > -1 ? desc.substring (bt+1, dot).trim () : desc.substring (bt+1).trim (); + other = app.getDbMapping (otherType); + if (other == null) + throw new RuntimeException ("DbMapping for "+otherType+" not found from "+home.typename); + localField = desc.substring (0, bt).trim (); + remoteField = dot < 0 ? null : desc.substring (dot+1).trim (); + if (mountpoint) prototype = otherType; + } else if (desc.indexOf (".") > -1) { + direction = DIRECT; + int dot = desc.indexOf ("."); + String otherType = desc.substring (0, dot).trim (); + other = app.getDbMapping (otherType); + if (other == null) + throw new RuntimeException ("DbMapping for "+otherType+" not found from "+home.typename); + remoteField = desc.substring (dot+1).trim (); + localField = null; + if (mountpoint) prototype = otherType; + } else { + if (virtual) { + direction = DIRECT; + other = app.getDbMapping (desc); + if (other == null) + throw new RuntimeException ("DbMapping for "+desc+" not found from "+home.typename); + remoteField = localField = null; + if (mountpoint) prototype = desc; + } else { + direction = PRIMITIVE; + localField = desc.trim (); + } + } + } + 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 group by property + groupby = props.getProperty (propname+".groupby"); + if (groupby != null && groupby.trim().length() == 0) + groupby = null; + if (groupby != null) { + groupbyorder = props.getProperty (propname+".groupby.order"); + if (groupbyorder != null && groupbyorder.trim().length() == 0) + groupbyorder = null; + } + // check if subnode condition should be applied for property relations + if ("_properties".equalsIgnoreCase (propname) || virtual) { + String subnodes2props = props.getProperty (propname+".aresubnodes"); + subnodesAreProperties = "true".equalsIgnoreCase (subnodes2props); + if (virtual) { + String subnodefilter = props.getProperty (propname+".subnoderelation"); + if (subnodefilter != null) { + filter = new Relation (subnodefilter, propname+".subnoderelation", home, props); + filter.groupby = groupby; + } + } + } + } + + public boolean isReference () { + return direction > PRIMITIVE; + } + + public boolean usesPrimaryKey () { + if (other == null) + return false; + if (remoteField == null) + // if remote field is null, it is assumed that it points to the primary key + return true; + return remoteField.equalsIgnoreCase (other.getIDField()); + } + + public Relation getFilter () { + return filter; + } + + /** + * Gets a key string to cache a node with a specific value for this relation. If the + * Relation uses the primary key return just the key value, otherwise include info on the + * used column or even the base node to avoid collisions. + */ + public String getKeyID (INode home, String kval) { + // if the column is not the primary key, we add the column name to the key + if ((direction == DIRECT || direction == FORWARD) && !usesPrimaryKey ()) { + // check if the subnode relation also has to be considered + if (subnodesAreProperties) + return "["+home.getID()+"]"+remoteField+"="+kval; // HACK + else + return remoteField+"="+kval; + } else { + return kval; + } + } + + /** + * Get the local column name for this relation to use in where clauses of select statements. + * This uses the home node's id as fallback if local field is not specified. + */ + public String getLocalField () { + // only assume local field is primary key if other objects "point" to this object + if (localField == null && direction == BACKWARD) + return home.getIDField (); + return localField; + } + + /** + * Return the local field name for updates. This is the same as getLocalField, but does not return the + * primary key name as a fallback. + */ + public String getDbField () { + return localField; + } + + /** + * Get the "remote" column name for this relation. Uses the remote node's id as fallback if the remote field is not specified. + */ + public String getRemoteField () { + // only assume remote field is primary key if this relation "points" to an object + if (remoteField == null && direction == FORWARD) + return other.getIDField (); + return remoteField; + } + + + /** + * Return a Relation that defines the subnodes of a virtual node. + */ + public Relation getVirtualSubnodeRelation () { + if (!virtual) + throw new RuntimeException ("getVirtualSubnodeRelation called on non-virtual relation"); + if (filter != null) + return filter; + Relation vr = new Relation (other, localField, remoteField, direction, subnodesAreProperties); + vr.groupby = groupby; + vr.groupbyorder = groupbyorder; + vr.order = order; + vr.filter = filter; + return vr; + } + + /** + * Return a Relation that defines the properties of a virtual node. + */ + public Relation getVirtualPropertyRelation () { + if (!virtual) + throw new RuntimeException ("getVirtualPropertyRelation called on non-virtual relation"); + Relation vr = new Relation (other, localField, remoteField, direction, subnodesAreProperties); + vr.groupby = groupby; + vr.groupbyorder = groupbyorder; + vr.order = order; + vr.filter = filter; + return vr; + } + + /** + * Return a Relation that defines the subnodes of a group-by node. + */ + public Relation getGroupbySubnodeRelation () { + if (groupby == null) + throw new RuntimeException ("getGroupbyPropertyRelation called on non-group-by relation"); + if (filter != null) + return filter; + Relation vr = new Relation (other, localField, remoteField, direction, true); + vr.order = order; + return vr; + } + + /** + * Return a Relation that defines the properties of a group-by node. + */ + public Relation getGroupbyPropertyRelation () { + if (groupby == null) + throw new RuntimeException ("getGroupbyPropertyRelation called on non-group-by relation"); + Relation vr = new Relation (other, localField, remoteField, direction, true); + vr.order = order; + return vr; + } + + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/util/SystemProperties.java b/src/helma/objectmodel/SystemProperties.java similarity index 75% rename from src/helma/util/SystemProperties.java rename to src/helma/objectmodel/SystemProperties.java index 01c0a4e4..46ea658b 100644 --- a/src/helma/util/SystemProperties.java +++ b/src/helma/objectmodel/SystemProperties.java @@ -1,8 +1,9 @@ // Property.java // Copyright (c) Hannes Wallnöfer 1997-2000 -package helma.util; +package helma.objectmodel; +import helma.util.*; import java.util.*; import java.io.*; @@ -19,27 +20,11 @@ public final class SystemProperties extends Properties { private File file; private long lastread, lastcheck; - final static long cacheTime = 1500l; - public SystemProperties () { this (null, null); } - 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 (String filename) { this (filename, null); } @@ -49,7 +34,7 @@ public final class SystemProperties extends Properties { } public SystemProperties (String filename, Properties defaultProps) { - // System.err.println ("building sysprops with file "+filename+" and node "+node); + // IServer.getLogger().log ("building sysprops with file "+filename+" and node "+node); this.defaultProps = defaultProps; props = defaultProps == null ? new Properties () : new Properties (defaultProps); @@ -79,7 +64,7 @@ public final class SystemProperties extends Properties { load (bpin); bpin.close (); } catch (Exception x) { - System.err.println ("Error reading properties from file "+file+": "+x); + IServer.getLogger().log ("Error reading properties from file "+file+": "+x); } lastread = System.currentTimeMillis (); props = newProps; @@ -124,42 +109,30 @@ public final class SystemProperties extends Properties { } public String getProperty (String name) { - if (System.currentTimeMillis () - lastcheck > cacheTime) + if (System.currentTimeMillis () - lastcheck > 1500l) checkFile (); return props.getProperty (name.toLowerCase()); } public String getProperty (String name, String defaultValue) { - if (System.currentTimeMillis () - lastcheck > cacheTime) + if (System.currentTimeMillis () - lastcheck > 1500l) checkFile (); return props.getProperty (name.toLowerCase(), defaultValue); } public Enumeration keys () { - if (System.currentTimeMillis () - lastcheck > cacheTime) + if (System.currentTimeMillis () - lastcheck > 1500l) checkFile (); return props.keys(); } public Enumeration elements () { - if (System.currentTimeMillis () - lastcheck > cacheTime) + if (System.currentTimeMillis () - lastcheck > 1500l) checkFile (); return props.elements(); } - public int size() { - if (System.currentTimeMillis () - lastcheck > cacheTime) - checkFile (); - return props.size(); - } - - public String toString () { - return props.toString (); - } - } - - diff --git a/src/helma/main/ApplicationManager.java b/src/helma/objectmodel/db/ApplicationManager.java similarity index 61% rename from src/helma/main/ApplicationManager.java rename to src/helma/objectmodel/db/ApplicationManager.java index 3cbb0d05..ac11a41d 100644 --- a/src/helma/main/ApplicationManager.java +++ b/src/helma/objectmodel/db/ApplicationManager.java @@ -1,10 +1,9 @@ // ApplicationManager.java // Copyright (c) Hannes Wallnöfer 1998-2000 -package helma.main; +package helma.objectmodel.db; -import java.util.Hashtable; -import java.util.Enumeration; +import java.util.*; import java.io.*; import java.lang.reflect.*; import java.rmi.*; @@ -13,27 +12,27 @@ import helma.framework.*; import helma.framework.core.*; import helma.objectmodel.*; import helma.servlet.*; -import helma.util.SystemProperties; import Acme.Serve.*; import javax.servlet.Servlet; /** - * This class is responsible for starting and stopping Helma applications. + * This class is responsible for starting and stopping HOP applications. */ public class ApplicationManager { private Hashtable applications; private int port; - private File hopHome; + private File appHome, dbHome; private SystemProperties props; private Server server; private long lastModified; - public ApplicationManager (int port, File hopHome, SystemProperties props, Server server) { + public ApplicationManager (int port, File appHome, File dbHome, SystemProperties props, Server server) { this.port = port; - this.hopHome = hopHome; + this.appHome = appHome; + this.dbHome = dbHome; this.props = props; this.server = server; applications = new Hashtable (); @@ -60,65 +59,60 @@ public class ApplicationManager { } } } catch (Exception mx) { - Server.getLogger().log ("Error starting applications: "+mx); + IServer.getLogger().log ("Error starting applications: "+mx); } lastModified = System.currentTimeMillis (); } } - void start (String appName) { - Server.getLogger().log ("Building application "+appName); + + private void start (String appName) { + IServer.getLogger().log ("Building application "+appName); try { - Application app = new Application (appName, hopHome, Server.sysProps, Server.dbProps); + Application app = new Application (appName, dbHome, appHome); applications.put (appName, app); // if we're running with the embedded web server, set app base uri to /appname - if (server.websrv != null && !"base".equalsIgnoreCase (appName)) + if (server.websrv != null) app.setBaseURI ("/"+java.net.URLEncoder.encode (appName)); - // the application is started later in the register method, when it's bound - app.init (); + app.start (); } catch (Exception x) { - Server.getLogger().log ("Error creating application "+appName+": "+x); + IServer.getLogger().log ("Error creating application "+appName+": "+x); x.printStackTrace (); } } - void stop (String appName) { - Server.getLogger().log ("Stopping application "+appName); + private void stop (String appName) { + IServer.getLogger().log ("Stopping application "+appName); try { Application app = (Application) applications.get (appName); if (server.websrv == null) { Naming.unbind ("//:"+port+"/"+appName); } else { - // server.websrv.removeServlet ("/"+appName+"/"); + server.websrv.removeServlet ("/"+appName+"/"); server.websrv.removeServlet ("/"+appName+"/*"); } app.stop (); - Server.getLogger().log ("Unregistered application "+appName); + IServer.getLogger().log ("Unregistered application "+appName); } catch (Exception x) { - Server.getLogger().log ("Couldn't unregister app: "+x); + IServer.getLogger().log ("Couldn't unregister app: "+x); } applications.remove (appName); } - void register (String appName) { + private void register (String appName) { try { - Server.getLogger().log ("Binding application "+appName); + IServer.getLogger().log ("Binding application "+appName); Application app = (Application) applications.get (appName); if (server.websrv == null) { Naming.rebind ("//:"+port+"/"+appName, app); } else { AcmeServletClient servlet = new AcmeServletClient (app); - if ("base".equalsIgnoreCase (appName)) - server.websrv.setDefaultServlet (servlet); - else { - // server.websrv.addServlet ("/"+appName+"/", servlet); - server.websrv.addServlet ("/"+appName+"/*", servlet); - } + server.websrv.addServlet ("/"+appName+"/", servlet); + server.websrv.addServlet ("/"+appName+"/*", servlet); } - app.start (); } catch (Exception x) { - Server.getLogger().log ("Couldn't register and start app: "+x); + IServer.getLogger().log ("Couldn't register app: "+x); } } @@ -134,27 +128,16 @@ public class ApplicationManager { } if (server.websrv != null) { File staticContent = new File (server.getHopHome(), "static"); - Server.getLogger().log("Serving static content from "+staticContent.getAbsolutePath()); + IServer.getLogger().log("Serving static content from "+staticContent.getAbsolutePath()); AcmeFileServlet fsrv = new AcmeFileServlet (staticContent); server.websrv.addServlet ("/static/", fsrv); server.websrv.addServlet ("/static/*", fsrv); } lastModified = System.currentTimeMillis (); } catch (Exception mx) { - Server.getLogger().log ("Error starting applications: "+mx); + IServer.getLogger().log ("Error starting applications: "+mx); mx.printStackTrace (); } } - /** - * Get an enumeration of all currently running applications. - */ - public Object[] getApplications () { - return applications.values ().toArray (); - } - - public Application getApplication(String name) { - return (Application)applications.get(name); - } - } diff --git a/src/helma/objectmodel/db/DbKey.java b/src/helma/objectmodel/db/DbKey.java deleted file mode 100644 index ff8b5041..00000000 --- a/src/helma/objectmodel/db/DbKey.java +++ /dev/null @@ -1,130 +0,0 @@ -// DbKey.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - -package helma.objectmodel.db; - -import java.io.Serializable; - - -/** - * This is the internal representation of a database key. It is constructed - * out of the database URL, the table name, the user name and the database - * key of the node and unique within each Helma application. 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; - - // lazily initialized hashcode - private transient int hashcode = 0; - - /** - * 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; - try { - DbKey k = (DbKey) what; - return (storageName == k.storageName || storageName.equals (k.storageName)) && - (id == k.id || id.equals (k.id)); - } catch (Exception x) { - return false; - } - } - - public int hashCode () { - if (hashcode == 0) { - hashcode = storageName == null ? - 17 + 37*id.hashCode () : - 17 + 37*storageName.hashCode() + +37*id.hashCode (); - } - return hashcode; - } - - public Key getParentKey () { - return null; - } - - public String getStorageName () { - return storageName; - } - - public String getID () { - return id; - } - - 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 deleted file mode 100644 index b2ac7a06..00000000 --- a/src/helma/objectmodel/db/DbMapping.java +++ /dev/null @@ -1,725 +0,0 @@ -// DbMapping.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - -package helma.objectmodel.db; - -import helma.framework.core.Application; -import helma.util.Updatable; -import helma.util.SystemProperties; -import java.util.HashMap; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.StringTokenizer; -import java.sql.*; -import com.workingdogs.village.*; - -/** - * 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 class DbMapping implements Updatable { - - // DbMappings belong to an application - Application app; - // prototype name of this mapping - String typename; - - int version; - - // properties from where the mapping is read - SystemProperties props; - - // name of data source to which this mapping writes - DbSource source; - // name of datasource - String sourceName; - // name of db table - String table; - - // list of properties to try for parent - ParentInfo[] parent; - - // Relations describing subnodes and properties. - Relation subnodesRel; - Relation propertiesRel; - - // if this defines a subnode mapping with groupby layer, we need a DbMapping for those groupby nodes - DbMapping groupbyMapping; - - // Map of property names to Relations objects - HashMap prop2db; - // Map of db columns to Relations objects - HashMap db2prop; - - // 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; - - // db field that specifies the prototype of an object - String prototypeField; - - // descriptor for key generation method - private String idgen; - // remember last key generated for this table - long lastID; - - // the (village) schema of the database table - Schema schema = null; - // the (village) keydef of the db table - KeyDef keydef = null; - - // timestamp of last modification of the mapping (type.properties) - long lastTypeChange; - // timestamp of last modification of an object of this type - long lastDataChange; - - /** - * Create an empty DbMapping - */ - public DbMapping (Application app) { - - this.app = app; - - prop2db = new HashMap (); - db2prop = new HashMap (); - - parent = null; - - idField = null; - } - - /** - * Create a DbMapping from a type.properties property file - */ - public DbMapping (Application app, String typename, SystemProperties props) { - - this.app = app; - this.typename = typename; - - prop2db = new HashMap (); - db2prop = new HashMap (); - - parent = null; - - idField = null; - - this.props = props; - update (); - - app.putDbMapping (typename, this); - } - - /** - * Tell the type manager whether we need update() to be called - */ - 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 () { - - // determin file format version of type.properties file - String versionInfo = props.getProperty ("_version"); - if ("1.2".equals (versionInfo)) - version = 1; - else - version = 0; - - table = props.getProperty (version == 0 ? "_tablename" : "_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"); - - sourceName = props.getProperty (version == 0 ? "_datasource" : "_db"); - if (sourceName != null) { - source = app.getDbSource (sourceName); - if (source == null) { - app.logEvent ("*** Data Source for prototype "+typename+" does not exist: "+sourceName); - app.logEvent ("*** accessing or storing a "+typename+" object will cause an error."); - } - } - - // 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"); - - nameField = props.getProperty ("_name"); - - protoField = props.getProperty ("_prototype"); - - String parentMapping = props.getProperty ("_parent"); - if (parentMapping != null) { - // comma-separated list of properties to be used as parent - StringTokenizer st = new StringTokenizer (parentMapping, ",;"); - parent = new ParentInfo[st.countTokens()]; - for (int i=0; i "+dbField); - } - } catch (Exception x) { - app.logEvent ("Error in type.properties: "+x.getMessage ()); - } - } - - prop2db = p2d; - db2prop = d2p; - - if (version == 1) { - String subnodeMapping = props.getProperty ("_children"); - if (subnodeMapping != null) { - try { - // check if subnode relation already exists. If so, reuse it - if (subnodesRel == null) - subnodesRel = new Relation (subnodeMapping, "_children", this, props); - subnodesRel.update (subnodeMapping, props, version); - if (subnodesRel.accessor != null) - propertiesRel = subnodesRel; - - } catch (Exception x) { - app.logEvent ("Error reading _subnodes relation for "+typename+": "+x.getMessage ()); - // subnodesRel = null; - } - } else - subnodesRel = null; - } else { - String subnodeMapping = props.getProperty ("_subnodes"); - if (subnodeMapping != null) { - try { - // check if subnode relation already exists. If so, reuse it - if (subnodesRel == null) - subnodesRel = new Relation (subnodeMapping, "_subnodes", this, props); - subnodesRel.update (subnodeMapping, props, version); - - } catch (Exception x) { - app.logEvent ("Error reading _subnodes relation for "+typename+": "+x.getMessage ()); - // subnodesRel = null; - } - } else - subnodesRel = null; - - String propertiesMapping = props.getProperty ("_properties"); - if (propertiesMapping != null) { - try { - // check if property relation already exists. If so, reuse it - if (propertiesRel == null) - propertiesRel = new Relation (propertiesMapping, "_properties", this, props); - propertiesRel.update (propertiesMapping, props, version); - - // take over groupby flag from subnodes, if properties are subnodes - if (propertiesRel.subnodesAreProperties && subnodesRel != null) { - propertiesRel.groupby = subnodesRel.groupby; - propertiesRel.constraints = subnodesRel.constraints; - propertiesRel.filter = subnodesRel.filter; - } - - } catch (Exception x) { - app.logEvent ("Error reading _properties relation for "+typename+": "+x.getMessage ()); - // propertiesRel = null; - } - } else - propertiesRel = null; - } - - if (groupbyMapping != null) { - initGroupbyMapping (); - groupbyMapping.lastTypeChange = this.lastTypeChange; - } - } - - - /** - * Get a JDBC connection for this DbMapping. - */ - public Connection getConnection () throws ClassNotFoundException, SQLException { - // if source was previously not available, check again - if (source == null && sourceName != null) - source = app.getDbSource (sourceName); - if (sourceName == null && parentMapping != null) - return parentMapping.getConnection (); - if (source == null) { - if (sourceName == null) - throw new SQLException ("Tried to get Connection from non-relational embedded data source."); - else - throw new SQLException ("Datasource is not defined: "+sourceName+"."); - } - return source.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 (source == null && parentMapping != null) - return parentMapping.getDbSource (); - return source; - } - - /** - * Get the URL of the data source used for this mapping. - */ - public String getSourceID () { - if (source == null && parentMapping != null) - return parentMapping.getSourceID (); - return source == null ? "" : source.url; - } - - /** - * Get the table name used for this type mapping. - */ - public String getTableName () { - if (source == null && parentMapping != null) - return parentMapping.getTableName (); - return table; - } - - /** - * Get the application this DbMapping belongs to. - */ - public Application getApplication () { - return app; - } - - /** - * Get the name of this mapping's application - */ - public String getAppName () { - return app.getName(); - } - - /** - * Get the name of the object type this DbMapping belongs to. - */ - public String getTypeName () { - return typename; - } - - /** - * Get the name of this type's parent type, if any. - */ - 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; - } - - /** - * 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; - } - - /** - * Get the column used for names of prototype. - */ - public String getPrototypeField () { - if (protoField == null && parentMapping != null) - return parentMapping.getPrototypeField (); - return protoField; - } - - - /** - * Translate a database column name to an object property name according to this mapping. - */ - public String columnNameToProperty (String columnName) { - if (columnName == null) - return null; - if (table == null && parentMapping != null) - return parentMapping.columnNameToProperty (columnName); - Relation rel = (Relation) db2prop.get (columnName.toUpperCase ()); - 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; - if (table == null && parentMapping != null) - return parentMapping.propertyToColumnName (propName); - Relation rel = (Relation) prop2db.get (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; - if (table == null && parentMapping != null) - return parentMapping.columnNameToRelation (columnName); - return (Relation) db2prop.get (columnName.toUpperCase ()); - } - - /** - * Translate an object property name to a database column name according to this mapping. - */ - public Relation propertyToRelation (String propName) { - if (propName == null) - return null; - if (table == null && parentMapping != null) - return parentMapping.propertyToRelation (propName); - return (Relation) prop2db.get (propName); - } - - - /** - * This returns the parent info array, which tells an object of this type how to - * determine its parent object. - */ - public synchronized ParentInfo[] getParentInfo () { - if (parent == null && parentMapping != null) - return parentMapping.getParentInfo (); - return parent; - } - - - public DbMapping getSubnodeMapping () { - if (subnodesRel != null) - return subnodesRel.otherType; - if (parentMapping != null) - return parentMapping.getSubnodeMapping (); - return null; - } - - - public DbMapping getExactPropertyMapping (String propname) { - Relation rel = getExactPropertyRelation (propname); - 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; - } - - /** - * 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 (subnodesRel == null || subnodesRel.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 (subnodesRel.groupbyprototype != null) - groupbyMapping.parentMapping = app.getDbMapping (subnodesRel.groupbyprototype); - groupbyMapping.subnodesRel = subnodesRel.getGroupbySubnodeRelation (); - if (propertiesRel != null) - groupbyMapping.propertiesRel = propertiesRel.getGroupbyPropertyRelation (); - else - groupbyMapping.propertiesRel = subnodesRel.getGroupbyPropertyRelation (); - groupbyMapping.typename = subnodesRel.groupbyprototype; - } - - /* public void setPropertyMapping (DbMapping pm) { - properties = pm; - } */ - - /* public void setSubnodeRelation (Relation rel) { - subnodesRel = rel; - } */ - - public void setPropertyRelation (Relation rel) { - propertiesRel = rel; - } - - public Relation getSubnodeRelation () { - if (subnodesRel == null && parentMapping != null) - return parentMapping.getSubnodeRelation (); - return subnodesRel; - } - - public Relation getPropertyRelation () { - if (propertiesRel == null && parentMapping != null) - return parentMapping.getPropertyRelation (); - return propertiesRel; - } - - 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; - } - - public String getSubnodeGroupby () { - if (subnodesRel == null && parentMapping != null) - return parentMapping.getSubnodeGroupby (); - return subnodesRel == null ? null : subnodesRel.groupby; - } - - public String getIDgen () { - if (idgen == null && parentMapping != null) - return parentMapping.getIDgen (); - return idgen; - } - - - public WrappedNodeManager getWrappedNodeManager () { - if (app == null) - throw new RuntimeException ("Can't get node manager from internal db mapping"); - return app.getWrappedNodeManager (); - } - - /** - * Tell whether this data mapping maps to a relational database table. This returns true - * if a datasource is specified, even if it is not a valid one. Otherwise, objects with invalid - * mappings would be stored in the embedded db instead of an error being thrown, which is - * not what we want. - */ - public boolean isRelational () { - if (sourceName != null) - return true; - if (parentMapping != null) - return parentMapping.isRelational (); - return false; - } - - /** - * Return a Village Schema object for this DbMapping. - */ - public synchronized Schema getSchema () throws ClassNotFoundException, SQLException, DataSetException { - if (!isRelational ()) - throw new SQLException ("Can't get Schema for non-relational data mapping"); - if (source == null && parentMapping != null) - return parentMapping.getSchema (); - // Use local variable s to avoid synchronization (schema may be nulled elsewhere) - Schema s = schema; - if (s != null) - return s; - schema = new Schema ().schema (getConnection (), table, "*"); - return schema; - } - - /** - * Return true if the column identified by the parameter is a string type. This is - * used in query building to determine if a value needs to be quoted. - */ - public boolean isStringColumn (String columnName) throws SQLException { - try { - Schema s = getSchema (); - if (s == null) - throw new SQLException ("Error retrieving relational schema for "+this); - Column c = s.getColumn (columnName); - if (c == null) - throw new SQLException ("Column "+columnName+" not found in "+this); - return c.isString () || c.isVarBinary () || c.isLongVarBinary (); - } catch (Exception x) { - throw new SQLException (x.getMessage ()); - } - } - - /** - * Return a Village Schema object for this DbMapping. - */ - public synchronized KeyDef getKeyDef () { - if (!isRelational ()) - throw new RuntimeException ("Can't get KeyDef for non-relational data mapping"); - if (source == null && parentMapping != null) - return parentMapping.getKeyDef (); - // Use local variable s to avoid synchronization (keydef may be nulled elsewhere) - KeyDef k = keydef; - if (k != null) - return k; - keydef = new KeyDef ().addAttrib (getIDField ()); - return keydef; - } - - public String toString () { - if (typename == null) - return "[unspecified internal DbMapping]"; - else - return ("["+app.getName()+"."+typename+"]"); - } - - public long getLastTypeChange () { - return lastTypeChange; - } - - - public long getLastDataChange () { - return lastDataChange; - } - - public void notifyDataChange () { - lastDataChange = System.currentTimeMillis (); - if (parentMapping != null && source == null) - parentMapping.notifyDataChange (); - } - - public synchronized long getNewID (long dbmax) { - if (parentMapping != null && source == null) - return parentMapping.getNewID (dbmax); - lastID = Math.max (dbmax+1, lastID+1); - return lastID; - } - - public HashMap getProp2DB () { - if (table == null && parentMapping != null) - return parentMapping.getProp2DB (); - return prop2db; - } - - public Iterator getDBPropertyIterator () { - if (table == null && parentMapping != null) - return parentMapping.getDBPropertyIterator (); - return db2prop.values ().iterator (); - } - - /** - * Return the name of the prototype which specifies the storage location - * (dbsource + tablename) for this type, or null if it is stored in the embedded - * db. - */ - public String getStorageTypeName () { - if (table == null && parentMapping != null) - return parentMapping.getStorageTypeName (); - return sourceName == null ? null : typename; - } - - /** - * Tell if another DbMapping is storage-compatible to this one, i.e. it is stored in the same table or - * embedded database. - */ - public boolean isStorageCompatible (DbMapping other) { - if (other == null) - return !isRelational (); - if (isRelational ()) - return getTableName().equals (other.getTableName ()) && - getDbSource().equals (other.getDbSource ()); - return !other.isRelational (); - } - - /** - * Return true if this db mapping represents the prototype indicated - * by the string argument, either itself or via one of its parent prototypes. - */ - public boolean isInstanceOf (String other) { - if (typename != null && typename.equals (other)) - return true; - DbMapping p = parentMapping; - while (p != null) { - if (p.typename != null && p.typename.equals (other)) - return true; - p = p.parentMapping; - } - return false; - } - - - public DbMapping getParentMapping () { - return parentMapping; - } - -} - diff --git a/src/helma/objectmodel/db/DbSource.java b/src/helma/objectmodel/db/DbSource.java deleted file mode 100644 index 5e0a5c4c..00000000 --- a/src/helma/objectmodel/db/DbSource.java +++ /dev/null @@ -1,120 +0,0 @@ -// DbSource.java -// Copyright (c) Hannes Wallnöfer 1999-2000 - -package helma.objectmodel.db; - -import java.sql.*; -import java.util.Hashtable; -import helma.util.SystemProperties; - -/** - * This class describes a releational data source (URL, driver, user and password). - */ - -public class DbSource { - - private String name; - private SystemProperties props; - private static SystemProperties defaultProps = null; - protected String url; - private String driver; - protected String user; - private String password; - - private long lastRead = 0l; - - public DbSource (String name, SystemProperties props) throws ClassNotFoundException { - this.name = name; - this.props = props; - init (); - } - - 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); - ////////////////////////////////////////////// - /* 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; - } - - 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; - } - - public String getName () { - return name; - } - - public static void setDefaultProps (SystemProperties props) { - defaultProps = props; - } - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/objectmodel/db/DbWrapper.java b/src/helma/objectmodel/db/DbWrapper.java index 38435894..c9c81a5c 100644 --- a/src/helma/objectmodel/db/DbWrapper.java +++ b/src/helma/objectmodel/db/DbWrapper.java @@ -24,15 +24,13 @@ public class DbWrapper { volatile long txncount=0; private File dbBaseDir; - private NodeManager nmgr; private String dbHome; - public DbWrapper (String dbHome, String dbFilename, NodeManager nmgr, boolean useTx) throws DbException { - - this.dbHome = dbHome; - this.nmgr = nmgr; + public DbWrapper (String dbHome, String dbFilename, boolean useTx) throws DbException { try { + + this.dbHome = dbHome; dbBaseDir = new File (dbHome); if (!dbBaseDir.exists()) dbBaseDir.mkdirs(); @@ -73,12 +71,12 @@ public class DbWrapper { loaded = true; } catch (NoClassDefFoundError noclass) { - nmgr.app.logEvent ("Warning: Using internal file based db as fallback."); - nmgr.app.logEvent ("Reason: "+noclass); + Server.getLogger().log ("Warning: Using internal file based db as fallback."); + Server.getLogger().log ("Reason: "+noclass); loaded = false; } catch (UnsatisfiedLinkError nolib) { - nmgr.app.logEvent ("Warning: Using internal file based db as fallback."); - nmgr.app.logEvent ("Reason: "+nolib); + Server.getLogger().log ("Warning: Using internal file based db as fallback."); + Server.getLogger().log ("Reason: "+nolib); loaded = false; } @@ -91,7 +89,7 @@ public class DbWrapper { // closing the dbenv leads to segfault when app is restarted // dbenv.close (0); // dbenv.remove (dbHome, Db.DB_FORCE); - nmgr.app.logEvent ("Closed Berkeley DB"); + Server.getLogger ().log ("Closed Berkeley DB"); } } @@ -128,7 +126,7 @@ public class DbWrapper { dbenv.txn_checkpoint (0, 0, 0); // for berkeley 3.0, remove third 0 parameter txncount = 0; lastCheckpoint = now; - nmgr.app.logEvent ("Spent "+(System.currentTimeMillis()-now)+" in checkpoint"); + Server.getLogger().log ("Spent "+(System.currentTimeMillis()-now)+" in checkpoint"); } public IDGenerator getIDGenerator (DbTxn txn, String kstr) throws Exception { @@ -222,7 +220,7 @@ public class DbWrapper { value.set_size (vbuf.length); db.put (txn, key, value, 0); - // nmgr.app.logEvent ("saved "+obj+", size = "+vbuf.length); + // IServer.getLogger().log ("saved "+obj+", size = "+vbuf.length); } private void deleteFromDB (DbTxn txn, String kstr) throws Exception { diff --git a/src/helma/objectmodel/db/ExternalizableVector.java b/src/helma/objectmodel/db/ExternalizableVector.java index bcea489d..71f30a1d 100644 --- a/src/helma/objectmodel/db/ExternalizableVector.java +++ b/src/helma/objectmodel/db/ExternalizableVector.java @@ -4,14 +4,14 @@ package helma.objectmodel.db; import java.io.*; -import java.util.ArrayList; +import java.util.*; /** * 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 { +public class ExternalizableVector extends Vector implements Externalizable { static final long serialVersionUID = 2316243615310540423L; @@ -19,7 +19,7 @@ public class ExternalizableVector extends ArrayList implements Externalizable { try { int size = in.readInt (); for (int i=0; i= 3) + 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 0) { - setName (propvalue); - anonymous = false; - // nameProp = localrel.propName; - } else { - anonymous = true; - } + if (anonymous && parentmap != null) { + Relation prel = parentmap.getPropertyRelation(); + if (prel != null && prel.subnodesAreProperties && !prel.usesPrimaryKey ()) try { + Relation localrel = dbmap.columnNameToProperty (prel.getRemoteField ()); + String propvalue = getString (localrel.propname, false); + if (propvalue != null && propvalue.length() > 0) { + setName (propvalue); + anonymous = false; + // nameProp = localrel.propname; } - } catch (Exception ignore) { - // just fall back to default method - } + } catch (Exception ignore) {} // just fall back to ID } return anonymous || name == null || name.length() == 0 ? id : name; } @@ -484,7 +440,7 @@ public final class Node implements INode, Serializable { b.insert (0, divider); else divider = "/"; - b.insert (0, p.getElementName ()); + b.insert (0, p.getNameOrID ()); p = p.getParent (); loopWatch++; @@ -496,6 +452,24 @@ public final class Node implements INode, Serializable { return b.toString (); } + public INode[] getPath () { + int pathSize = 1; + INode p = getParent (); + + while (p != null) { + pathSize +=1; + p = p.getParent (); + if (pathSize > 100) // sanity check + break; + } + INode path[] = new INode[pathSize]; + p = this; + for (int i = pathSize-1; i>=0; i--) { + path[i] = p; + p = p.getParent (); + } + return path; + } public String getPrototype () { if (prototype == null && propMap != null) { @@ -515,7 +489,8 @@ public final class Node implements INode, Serializable { public void setDbMapping (DbMapping dbmap) { if (this.dbmap != dbmap) { this.dbmap = dbmap; - // primaryKey = null; + primaryKey = null; + ((Transactor) Thread.currentThread()).visitCleanNode (this); } } @@ -524,23 +499,11 @@ public final class Node implements INode, Serializable { } 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); + primaryKey = new Key (dbmap, id); return primaryKey; } - 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))) @@ -561,8 +524,7 @@ public final class Node implements INode, Serializable { 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 \"/\"."); + 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 @@ -574,7 +536,8 @@ public final class Node implements INode, Serializable { * the ID + DB map combo. */ public void setParent (Node parent) { - parentHandle = parent == null ? null : parent.getHandle (); + this.parentID = parent == null ? null : parent.getID(); + this.parentmap = parent == null ? null : parent.getDbMapping(); } /** @@ -584,31 +547,32 @@ public final class Node implements INode, Serializable { */ public void setParent (Node parent, String propertyName) { // we only do that for relational nodes. - if (!isRelational ()) + if (dbmap == null || !dbmap.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)) + String oldParentID = parentID; + parentID = parent == null ? null : parent.getID(); + parentmap = parent == null ? null : parent.getDbMapping(); + + if (parentID == null || parentID.equals (oldParentID)) // nothing changed, no need to find access property return; - if (parent != null && propertyName == null) { + if (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.accessor != null) { + if (prel != null && prel.other == dbmap && prel.direction == Relation.DIRECT) { // reverse look up property used to access this via parent - Relation proprel = dbmap.columnNameToRelation (prel.accessor); - if (proprel != null && proprel.propName != null) - newname = getString (proprel.propName, false); + String dbfield = prel.getRemoteField (); + if (dbfield != null) { + Relation proprel = (Relation) dbmap.db2prop.get (dbfield); + if (proprel != null && proprel.propname != null) + newname = getString (proprel.propname, false); + } } } @@ -626,66 +590,31 @@ public final class Node implements INode, Serializable { } - /** - * Get parent, retrieving it if necessary. - */ public INode getParent () { // check what's specified in the type.properties for this node. - ParentInfo[] parentInfo = null; - if (isRelational () && (lastParentSet < dbmap.getLastTypeChange() || lastParentSet < lastmodified)) - parentInfo = dbmap.getParentInfo (); + String[] parentProps = null; + if (dbmap != null && dbmap.isRelational ()) + parentProps = dbmap.getParentPropNames (); // check if current parent candidate matches presciption, if not, try to get it - if (parentInfo != null && state != TRANSIENT) { - for (int i=0; i i) + anonymous = ano[i].booleanValue(); + return pn; } } } // fall back to heuristic parent (the node that fetched this one from db) - if (parentHandle == null) + if (parentID == null) return null; - return parentHandle.getNode (nmgr); - } - - - /** - * Get parent, using cached info if it exists. - */ - public Node getCachedParent () { - if (parentHandle == null) - return null; - return parentHandle.getNode (nmgr); + return nmgr.getNode (parentID, parentmap); } @@ -703,25 +632,19 @@ public final class Node implements INode, Serializable { if (elem instanceof Node) node = (Node) elem; else - throw new RuntimeException ("Can't add fixed-transient node to a persistent node"); - - // only lock node if it has to be modified for a change in subnodes - if (!ignoreSubnodeChange ()) - checkWriteLock (); - node.checkWriteLock (); - - // if subnodes are defined via realation, make sure its constraints are enforced. - if (dbmap != null && dbmap.getSubnodeRelation () != null) - dbmap.getSubnodeRelation ().setConstraints (this, node); - + node = convert (elem); // if the new node is marked as TRANSIENT and this node is not, mark new node as NEW if (state != TRANSIENT && node.state == TRANSIENT) node.makePersistentCapable (); String n = node.getName(); - - // if (n.indexOf('/') > -1) - // throw new RuntimeException ("\"/\" found in Node name."); + if (n.indexOf('/') > -1) + throw new RuntimeException ("\"/\" found in Node name."); + + // only lock node if it has to be modified for a change in subnodes + if (!ignoreSubnodeChange ()) + checkWriteLock (); + node.checkWriteLock (); // only mark this node as modified if subnodes are not in relational db // pointing to this node. @@ -736,16 +659,16 @@ public final class Node implements INode, Serializable { if (dbmap != null) { Relation srel = dbmap.getSubnodeRelation (); if (srel != null && srel.groupby != null) try { - Relation groupbyRel = srel.otherType.columnNameToRelation (srel.groupby); + Relation groupbyRel = (Relation) srel.other.db2prop.get (srel.groupby); String groupbyProp = (groupbyRel != null) ? - groupbyRel.propName : srel.groupby; + groupbyRel.propname : srel.groupby; String groupbyValue = node.getString (groupbyProp, false); INode groupbyNode = getNode (groupbyValue, false); // if group-by node doesn't exist, we'll create it if (groupbyNode == null) groupbyNode = getGroupbySubnode (groupbyValue, true); groupbyNode.addNode (node); - + checkBackLink (node); return node; } catch (Exception x) { System.err.println ("Error adding groupby: "+x); @@ -757,25 +680,30 @@ public final class Node implements INode, Serializable { if (where < 0 || where > numberOfNodes ()) where = numberOfNodes (); + if (node.getParent () != null) { + // this makes the job of addLink, which means that addLink and addNode + // are functionally equivalent now. + if (node.getParent () != this || !node.anonymous) { + node.registerLink (this); + } + } - NodeHandle nhandle = node.getHandle (); - if (subnodes != null && subnodes.contains (nhandle)) { + if (subnodes != null && subnodes.contains (node.getID ())) { // Node is already subnode of this - just move to new position - subnodes.remove (nhandle); + subnodes.removeElement (node.getID ()); where = Math.min (where, numberOfNodes ()); - subnodes.add (where, nhandle); + subnodes.insertElementAt (node.getID (), where); } else { - if (subnodes == null) - subnodes = new ExternalizableVector (); - subnodes.add (where, nhandle); + if (subnodes == null) subnodes = new ExternalizableVector (); + subnodes.insertElementAt (node.getID (), where); // check if properties are subnodes (_properties.aresubnodes=true) if (dbmap != null && node.dbmap != null) { Relation prel = dbmap.getPropertyRelation(); - if (prel != null && prel.accessor != null) { - Relation localrel = node.dbmap.columnNameToRelation (prel.accessor); + if (prel != null && prel.subnodesAreProperties && !prel.usesPrimaryKey ()) { + Relation localrel = node.dbmap.columnNameToProperty (prel.getRemoteField ()); // if no relation from db column to prop name is found, assume that both are equal - String propname = localrel == null ? prel.accessor : localrel.propName; + String propname = localrel == null ? prel.getRemoteField() : localrel.propname; String prop = node.getString (propname, false); if (prop != null && prop.length() > 0) { INode old = getNode (prop, false); @@ -783,38 +711,47 @@ public final class Node implements INode, Serializable { unset (prop); removeNode (old); } + // throw new RuntimeException ("Property "+prop+" is already defined for "+this); 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); - } + if (node.getParent () == null && !"root".equalsIgnoreCase (node.getPrototype ())) { + node.setParent (this); + node.anonymous = true; } } + checkBackLink (node); + lastmodified = System.currentTimeMillis (); lastSubnodeChange = lastmodified; // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, node)); return node; } + private void checkBackLink (Node node) { + // check if the subnode is in relational db and needs to link back to this + // in order to make it a subnode + if (dbmap != null) { + Relation srel = dbmap.getSubnodeRelation (); + if (srel != null && srel.direction == Relation.BACKWARD) { + Relation backlink = srel.other.columnNameToProperty (srel.getRemoteField()); + if (backlink != null && backlink.propname != null) { + if (node.get (backlink.propname, false) == null) { + if (this.state == VIRTUAL) + node.setString (backlink.propname, getNonVirtualHomeID()); + else + node.setString (backlink.propname, getID()); + } + } + } + } + } public INode createNode () { - // create new node at end of subnode array - return createNode (null, numberOfNodes ()); + return createNode (null, numberOfNodes ()); // create new node at end of subnode array } public INode createNode (int where) { @@ -831,7 +768,7 @@ public final class Node implements INode, Serializable { boolean anon = false; if (nm == null || "".equals (nm.trim ())) anon = true; - Node n = new Node (nm, null, nmgr); + Node n = new Node (nm, nmgr); if (anon) addNode (n, where); else @@ -841,101 +778,78 @@ public final class Node implements INode, Serializable { /** - * 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. + * register a node that links to this node. */ - protected void registerLinkFrom (Node from) { - if (isRelational ()) - return; - if (from.getState () == TRANSIENT) - return; + protected void registerLink (Node from) { if (links == null) links = new ExternalizableVector (); - Object fromHandle = from.getHandle (); - if (!links.contains (fromHandle)) - links.add (fromHandle); + Object fromID = from.getID (); + if (!links.contains (fromID)) + links.addElement (fromID); } - /** - * This implements the getChild() method of the IPathElement interface - */ - public IPathElement getChildElement (String name) { - IPathElement child = (IPathElement) getSubnode (name); - if (child == null) - child = (IPathElement) getNode (name, false); - return child; - } + public INode getSubnode (String path) { + StringTokenizer st = new StringTokenizer (path, "/"); + Node retval = this, runner; - /** - * This implements the getParentElement() method of the IPathElement interface - */ - public IPathElement getParentElement () { - return getParent (); - } + while (st.hasMoreTokens () && retval != null) { + runner = retval; + String next = st.nextToken().trim().toLowerCase (); + if ("".equals (next)) { + retval = this; + } else { + runner.loadNodes (); + boolean found = runner.subnodes == null ? false : runner.subnodes.contains (next); - public INode getSubnode (String subid) { - // System.err.println ("GETSUBNODE : "+this+" > "+subid); - if ("".equals (subid)) { - return this; - } - - Node retval = null; - - if (subid != null) { - - 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 ())) { + if (srel != null && srel.groupby != null) + retval = nmgr.getNode (this, (String) subnodes.elementAt (index), srel); + else + retval = nmgr.getNode ((String) subnodes.elementAt (index), smap); + if (retval != null && retval.parentID == null && !"root".equalsIgnoreCase (retval.getPrototype ())) { retval.setParent (this); retval.anonymous = true; } @@ -948,65 +862,67 @@ public final class Node implements INode, Serializable { if (subnodes == null) subnodes = new ExternalizableVector (); - 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.contains (sid) || create) try { + Relation srel = dbmap.getSubnodeRelation (); + Relation prel = dbmap.getPropertyRelation (); + boolean relational = srel.other != null && srel.other.isRelational (); if (relational || create) { - Node node = relational ? new Node (this, sid, nmgr, null) : new Node ("groupby-"+sid, null, nmgr); + Node node = relational ? new Node (this, sid, nmgr, null) : new Node ("groupby-"+sid, nmgr); // set "groupname" property to value of groupby field node.setString ("groupname", sid); if (relational) { - node.setDbMapping (groupbyMapping); + DbMapping dbm = new DbMapping (); + Relation gsrel = srel.getGroupbySubnodeRelation(); + dbm.setSubnodeMapping (srel.other); + dbm.setSubnodeRelation (gsrel); + if (prel != null) { + dbm.setPropertyMapping (prel.other); + dbm.setPropertyRelation (prel.getGroupbyPropertyRelation()); + } + node.setDbMapping (dbm); + String snrel = "WHERE "+srel.groupby +"='"+sid+"'"; + if (gsrel.direction == Relation.BACKWARD) + snrel += " AND "+gsrel.getRemoteField()+"='"+getNonVirtualHomeID()+"'"; + if (gsrel.order != null) + snrel += " ORDER BY "+gsrel.order; + node.setSubnodeRelation (snrel); } else { setNode (sid, node); - subnodes.add (node.getHandle ()); + subnodes.addElement (node.getID ()); } - 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(); + IServer.getLogger ().log ("Error creating group-by node for "+sid+": "+noluck); } return null; } public boolean remove () { checkWriteLock (); - try { - if (!anonymous) - getParent ().unset (name); - else - getParent ().removeNode (this); - } catch (Exception x) { - return false; - } + if (anonymous) + getParent ().unset (name); + else + getParent ().removeNode (this); return true; } public void removeNode (INode node) { - // nmgr.logEvent ("removing: "+ node); + IServer.getLogger().log ("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 (); releaseNode (n); - - if (parent == this) { + if (n.getParent () == this) { n.deepRemoveNode (); } else { // removed just a link, not the main node. if (n.links != null) { - n.links.remove (getHandle ()); + n.links.removeElement (this.id); if (n.state == CLEAN) n.markAs (MODIFIED); } } @@ -1018,7 +934,7 @@ public final class Node implements INode, Serializable { */ protected void releaseNode (Node node) { if (subnodes != null) - subnodes.remove (node.getHandle ()); + subnodes.removeElement (node.getID ()); lastSubnodeChange = System.currentTimeMillis (); @@ -1026,20 +942,20 @@ public final class Node implements INode, Serializable { // which needs to be unset if (dbmap != null) { Relation srel = dbmap.getSubnodeRelation (); - /*if (srel != null && srel.reftype == Relation.BACKWARD) { - Relation backlink = srel.otherType.columnNameToRelation (srel.getRemoteField ()); - if (backlink != null && id.equals (node.getString (backlink.propName, false))) - node.unset (backlink.propName); - } */ + if (srel != null && srel.direction == Relation.BACKWARD) { + Relation backlink = srel.other.columnNameToProperty (srel.getRemoteField ()); + if (backlink != null && id.equals (node.getString (backlink.propname, false))) + node.unset (backlink.propname); + } } - // check if subnodes are also accessed as properties. If so, also unset the property + // check if subnodes are handled as virtual fs if (dbmap != null && node.dbmap != null) { Relation prel = dbmap.getPropertyRelation(); - if (prel != null && prel.accessor != null) { - Relation localrel = node.dbmap.columnNameToRelation (prel.accessor); + if (prel != null && prel.subnodesAreProperties && !prel.usesPrimaryKey ()) { + Relation localrel = node.dbmap.columnNameToProperty (prel.getRemoteField()); // if no relation from db column to prop name is found, assume that both are equal - String propname = localrel == null ? prel.accessor : localrel.propName; + String propname = localrel == null ? prel.getRemoteField () : localrel.propname; String prop = node.getString (propname, false); if (prop != null && getNode (prop, false) == node) unset (prop); @@ -1053,7 +969,7 @@ public final class Node implements INode, Serializable { // 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); + // IServer.getLogger().log ("released node "+node +" from "+this+" oldobj = "+what); if (state == CLEAN) markAs (MODIFIED); } @@ -1064,13 +980,23 @@ public final class Node implements INode, Serializable { */ protected void deepRemoveNode () { - // notify nodes that link to this node being deleted. + // notify nodes that link to this node that it is 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); + // TODO: solve dbmap problem + Node link = nmgr.getNode ((String) links.elementAt (i), null); + if (link != null) link.releaseNode (this); + } + + // clean up all nodes that use n as a property + if (proplinks != null) { + for (Enumeration e1 = proplinks.elements (); e1.hasMoreElements (); ) try { + String pid = (String) e1.nextElement (); + Node pnode = nmgr.getNode (pid, null); + if (pnode != null) { + IServer.getLogger().log("Warning: Can't unset node property of "+pnode.getFullName ()); + } + } catch (Exception ignore) {} } // tell all nodes that are properties of n that they are no longer used as such @@ -1086,16 +1012,16 @@ public final class Node implements INode, Serializable { // 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. + // removeElement modifies the Vector we are enumerating, so we are extra careful. for (Enumeration e3 = getSubnodes (); e3.hasMoreElements(); ) { - v.add (e3.nextElement()); + v.addElement (e3.nextElement()); } int m = v.size (); for (int i=0; i= lastSubnodeFetch || subnodes == null) { @@ -1201,7 +1124,7 @@ public final class Node implements INode, Serializable { // 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()); + return (rel != null && rel.other != null && rel.other.isRelational() && rel.direction == Relation.BACKWARD); } /** @@ -1209,17 +1132,13 @@ public final class Node implements INode, Serializable { */ public Enumeration properties () { - if (dbmap != null && dbmap.getProp2DB ().size() > 0) + if (dbmap != null && dbmap.prop2db.size() > 0) // return the properties defined in type.properties, if there are any - return new Enumeration () { - Iterator i = dbmap.getProp2DB().keySet().iterator(); - public boolean hasMoreElements() {return i.hasNext();} - public Object nextElement () {return i.next();} - }; + return dbmap.prop2db.keys(); Relation prel = dbmap == null ? null : dbmap.getPropertyRelation (); - if (prel != null && prel.accessor != null && !prel.subnodesAreProperties - && prel.otherType != null && prel.otherType.isRelational ()) + if (prel != null && prel.direction == Relation.DIRECT && !prel.subnodesAreProperties + && prel.other != null && prel.other.isRelational ()) // return names of objects from a relational db table return nmgr.getPropertyNames (this, prel).elements (); else if (propMap != null) @@ -1240,11 +1159,11 @@ public final class Node implements INode, Serializable { } public String getParentInfo () { - return "anonymous:"+anonymous+",parentHandle"+parentHandle+",parent:"+getParent(); + return "anonymous:"+anonymous+",parentID:"+parentID+",parentmap:"+parentmap+",parent:"+getParent(); } protected Property getProperty (String propname, boolean inherit) { - // nmgr.logEvent ("GETTING PROPERTY: "+propname); + // IServer.getLogger().log ("GETTING PROPERTY: "+propname); if (propname == null) return null; Property prop = propMap == null ? null : (Property) propMap.get (propname.toLowerCase ()); @@ -1254,59 +1173,45 @@ public final class Node implements INode, Serializable { DbMapping pmap = dbmap == null ? null : dbmap.getExactPropertyMapping (propname); if (pmap != null && prop != null && prop.type != IProperty.NODE) { // this is a relational node stored by id but we still think it's just a string. fix it - prop.nhandle = new NodeHandle (new DbKey (pmap, prop.getStringValue ())); + prop.nvalueID = prop.getStringValue (); prop.type = IProperty.NODE; } - // the property does not exist in our propmap - see if we can create it on the fly, - // either because it is mapped from a relational database or defined as virtual node + if (prop == null && dbmap != null) { Relation prel = dbmap.getPropertyRelation (propname); - Relation srel = dbmap.getSubnodeRelation (); - if (prel == null && srel != null && srel.groupby != null) - prel = srel; if (prel == null) prel = dbmap.getPropertyRelation (); - if (prel != null) { - // if what we want is a virtual node, fetch anyway. - if (state == TRANSIENT && prel.virtual) { - INode node = new Node (propname, prel.getPrototype (), nmgr); - node.setDbMapping (prel.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 && (prel.virtual || prel.accessor != null || prel.groupby != null)) { - // this may be a relational node stored by property name - try { - Node pn = nmgr.getNode (this, propname, prel); - 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); + if (prel != null && (prel.direction == Relation.DIRECT || prel.virtual)) { + // this *may* be a relational node stored by property name + try { + Node pn = nmgr.getNode (this, propname, prel); + if (pn != null) { + if (pn.parentID == null && !"root".equalsIgnoreCase (pn.getPrototype ())) { + pn.setParent (this); + pn.name = propname; + pn.anonymous = false; } - } catch (RuntimeException nonode) { - // wasn't a node after all + prop = new Property (propname, this, pn); } + } catch (RuntimeException nonode) { + // wasn't a node after all } } } - - if (prop == null && inherit && getParent () != null && state != TRANSIENT) { + if (prop == null && inherit && getParent () != null) { prop = ((Node) getParent ()).getProperty (propname, inherit); } return prop; } - /* public String getString (String propname, String defaultValue, boolean inherit) { + public String getString (String propname, String defaultValue, boolean inherit) { String propValue = getString (propname, inherit); return propValue == null ? defaultValue : propValue; - } */ + } public String getString (String propname, boolean inherit) { - // propname = propname.toLowerCase (); + propname = propname.toLowerCase (); Property prop = getProperty (propname, inherit); try { return prop.getStringValue (); @@ -1315,7 +1220,7 @@ public final class Node implements INode, Serializable { } public long getInteger (String propname, boolean inherit) { - // propname = propname.toLowerCase (); + propname = propname.toLowerCase (); Property prop = getProperty (propname, inherit); try { return prop.getIntegerValue (); @@ -1324,7 +1229,7 @@ public final class Node implements INode, Serializable { } public double getFloat (String propname, boolean inherit) { - // propname = propname.toLowerCase (); + propname = propname.toLowerCase (); Property prop = getProperty (propname, inherit); try { return prop.getFloatValue (); @@ -1333,7 +1238,7 @@ public final class Node implements INode, Serializable { } public Date getDate (String propname, boolean inherit) { - // propname = propname.toLowerCase (); + propname = propname.toLowerCase (); Property prop = getProperty (propname, inherit); try { return prop.getDateValue (); @@ -1343,7 +1248,7 @@ public final class Node implements INode, Serializable { public boolean getBoolean (String propname, boolean inherit) { - // propname = propname.toLowerCase (); + propname = propname.toLowerCase (); Property prop = getProperty (propname, inherit); try { return prop.getBooleanValue (); @@ -1352,7 +1257,7 @@ public final class Node implements INode, Serializable { } public INode getNode (String propname, boolean inherit) { - // propname = propname.toLowerCase (); + propname = propname.toLowerCase (); Property prop = getProperty (propname, inherit); try { return prop.getNodeValue (); @@ -1361,7 +1266,7 @@ public final class Node implements INode, Serializable { } public Object getJavaObject (String propname, boolean inherit) { - // propname = propname.toLowerCase (); + propname = propname.toLowerCase (); Property prop = getProperty (propname, inherit); try { return prop.getJavaObjectValue (); @@ -1370,7 +1275,7 @@ public final class Node implements INode, Serializable { } public void setString (String propname, String value) { - // nmgr.logEvent ("setting String prop"); + // IServer.getLogger().log ("setting String prop"); checkWriteLock (); if (propMap == null) @@ -1395,17 +1300,15 @@ public final class Node implements INode, Serializable { } // check if this may have an effect on the node's URL when using subnodesAreProperties - // 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 (); + INode parent = getParent (); - if (dbmap != null && parent != null && parent.getDbMapping() != null) { + if (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 (); + parentmap = parent.getDbMapping (); Relation prel = parentmap.getPropertyRelation (); - String dbcolumn = dbmap.propertyToColumnName (propname); - if (prel != null && prel.accessor != null && prel.accessor.equals (dbcolumn)) { + if (prel != null && prel.subnodesAreProperties && propname.equals (prel.getRemoteField())) { INode n = parent.getNode (value, false); if (n != null && n != this) { parent.unset (value); @@ -1418,7 +1321,7 @@ public final class Node implements INode, Serializable { 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)); + nmgr.evictKey (new Key (prel.other, prel.getKeyID (parent, oldvalue))); } } parent.setNode (value, this); @@ -1426,25 +1329,6 @@ public final class Node implements INode, Serializable { } } - // 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 (); @@ -1453,7 +1337,7 @@ public final class Node implements INode, Serializable { } public void setInteger (String propname, long value) { - // nmgr.logEvent ("setting bool prop"); + // IServer.getLogger().log ("setting bool prop"); checkWriteLock (); if (propMap == null) @@ -1476,7 +1360,7 @@ public final class Node implements INode, Serializable { } public void setFloat (String propname, double value) { - // nmgr.logEvent ("setting bool prop"); + // IServer.getLogger().log ("setting bool prop"); checkWriteLock (); if (propMap == null) @@ -1499,7 +1383,7 @@ public final class Node implements INode, Serializable { } public void setBoolean (String propname, boolean value) { - // nmgr.logEvent ("setting bool prop"); + // IServer.getLogger().log ("setting bool prop"); checkWriteLock (); if (propMap == null) @@ -1523,7 +1407,7 @@ public final class Node implements INode, Serializable { public void setDate (String propname, Date value) { - // nmgr.logEvent ("setting date prop"); + // IServer.getLogger().log ("setting date prop"); checkWriteLock (); if (propMap == null) @@ -1546,7 +1430,7 @@ public final class Node implements INode, Serializable { } public void setJavaObject (String propname, Object value) { - // nmgr.logEvent ("setting jobject prop"); + // IServer.getLogger().log ("setting jobject prop"); checkWriteLock (); if (propMap == null) @@ -1569,25 +1453,14 @@ public final class Node implements INode, Serializable { } 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); - 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()); - } - + // IServer.getLogger().log ("setting node prop"); 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"); - + n = convert (value); // 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 (); @@ -1596,18 +1469,27 @@ public final class Node implements INode, Serializable { // 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 && n.adoptName && !"root".equalsIgnoreCase (n.getPrototype ())) { + if (n.getParent () == null && n.adoptName && !"root".equalsIgnoreCase (n.getPrototype ())) { n.setParent (this); n.name = propname; n.anonymous = false; - } + // IServer.getLogger().log ("adopted named node: "+n.getFullName ()); + } // else IServer.getLogger().log ("not adopted: "+n.getFullName ()); + if (propMap == null) + propMap = new Hashtable (); propname = propname.trim (); String p2 = propname.toLowerCase (); - Property prop = propMap == null ? null : (Property) propMap.get (p2); + DbMapping nmap = dbmap == null ? null : dbmap.getPropertyMapping (propname); + if (nmap != null && nmap != n.getDbMapping()) { + n.setDbMapping (nmap); + n.setPrototype (nmap.getTypeName ()); + } + + Property prop = (Property) propMap.get (p2); if (prop != null) { - if (prop.type == IProperty.NODE && n.getHandle ().equals (prop.nhandle)) { + if (prop.type == IProperty.NODE && n.getID ().equals (prop.nvalueID)) { // nothing to do, just clean up locks and return if (state == CLEAN) clearWriteLock (); if (n.state == CLEAN) n.clearWriteLock (); @@ -1619,40 +1501,23 @@ public final class Node implements INode, Serializable { 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()) { + if (rel == null || rel.direction == Relation.FORWARD || rel.virtual || rel.other == null || !rel.other.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); - // if the field is not the primary key of the property, also register it - if (rel != null && rel.accessor != null && state != TRANSIENT) { - Key secKey = new SyntheticKey (getKey (), propname); - nmgr.evictKey (secKey); - tx.visitCleanNode (secKey, n); - } - } + } + String nID = n.getID(); + // check node in with transactor cache + Transactor tx = (Transactor) Thread.currentThread (); + tx.visitCleanNode (new Key (nmap, nID), n); + // if the field is not the primary key of the property, also register it + if (rel != null && !rel.getKeyID(this, p2).equals (nID)) + tx.visitCleanNode (new Key (rel.other, rel.getKeyID(this, p2)), n); + + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.SUBNODE_ADDED, n)); + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); lastmodified = System.currentTimeMillis (); + if (state == CLEAN) markAs (MODIFIED); if (n.state == DELETED) n.markAs (MODIFIED); } @@ -1677,6 +1542,200 @@ public final class Node implements INode, Serializable { } catch (Exception ignore) {} } + protected void registerPropLink (INode n) { + if (proplinks == null) + proplinks = new ExternalizableVector (); + String plid = n.getID (); + if (!proplinks.contains (plid)) + proplinks.addElement (n.getID ()); + if (state == CLEAN || state == DELETED) + markAs (MODIFIED); + } + + protected void unregisterPropLink (INode n) { + if (proplinks != null) + proplinks.removeElement (n.getID ()); + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.NODE_REMOVED)); + // Server.throwNodeEvent (new NodeEvent (n, NodeEvent.SUBNODE_REMOVED, this)); + if (state == CLEAN) + markAs (MODIFIED); + } + + + public void sanityCheck () { + checkSubnodes (); + checkProperties (); + checkLinks (); + checkPropLinks (); + if (getParent () == null && !"root".equals (name)) { + System.out.println ("*** parent: "+parentID+": "+this.getName ()); + } + } + + + private void checkLinks () { + Vector v = links == null ? null : (Vector) links.clone (); + int l = v == null ? 0 : v.size (); + for (int i = 0; i < l; i++) { + String k = (String) v.elementAt (i); + Node link = nmgr.getNode (k, null); + if (link == null) { + links.removeElement (k); + System.out.println ("**** link "+k+": "+this.getFullName ()); + markAs (MODIFIED); + } + } + } + + private void checkPropLinks () { + Vector v = proplinks == null ? null : (Vector) proplinks.clone (); + int l = v == null ? 0 : v.size (); + for (int i = 0; i < l; i++) { + String k = (String) v.elementAt (i); + Node link = nmgr.getNode (k, null); + if (link == null) { + proplinks.removeElement (k); + System.out.println ("**** proplink "+k+": "+this.getFullName ()); + markAs (MODIFIED); + } + } + } + + private void checkSubnodes () { + Vector v = subnodes == null ? null : (Vector) subnodes.clone (); + int l = v == null ? 0 : v.size (); + for (int i = 0; i < l; i++) { + String k = (String) v.elementAt (i); + Node link = nmgr.getNode (k, null); + if (link == null) { + subnodes.removeElement (k); + System.out.println ("**** subnode "+k+": "+this.getFullName ()); + markAs (MODIFIED); + } + } + } + + private void checkProperties () { + Hashtable ht = propMap == null ? new Hashtable () : (Hashtable) propMap.clone (); + for (Enumeration ps = ht.elements (); ps.hasMoreElements (); ) { + Property p = (Property) ps.nextElement (); + if (p.getType () == IProperty.NODE && p.getNodeValue () == null) { + System.out.println ("**** property "+p.propname+"->"+p.nvalueID+": "+this.getFullName ()); + // INode par = getParent (); + // if (par != null) + // par.removeNode (this); + // markAs (DELETED); + } + } + + } + + /** + * content-related + */ + + public boolean isText () throws IOException { + return getContentType().indexOf ("text/") == 0; + } + + public boolean isBinary () throws IOException { + return getContentType().indexOf ("text/") != 0; + } + + public String getContentType () { + if (contentType == null) + return "text/plain"; + return contentType; + } + + public void setContentType (String type) { + checkWriteLock (); + contentType = type; + lastmodified = System.currentTimeMillis (); + if (state == CLEAN) markAs (MODIFIED); + } + + public int getContentLength () { + if (content == null) + return 0; + return content.length; + } + + public void setContent (byte cnt[], String type) { + checkWriteLock (); + + if (type != null) + contentType = type; + content = cnt; + lastmodified = System.currentTimeMillis (); + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.CONTENT_CHANGED)); + if (state == CLEAN) markAs (MODIFIED); + } + + + public void setContent (String cstr) { + checkWriteLock (); + + content = cstr.getBytes (); + lastmodified = System.currentTimeMillis (); + // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.CONTENT_CHANGED)); + if (state == CLEAN) markAs (MODIFIED); + } + + public byte[] getContent () { + + if (content == null || content.length == 0) + return "".getBytes (); + + byte retval[] = new byte[content.length]; + System.arraycopy (content, 0, retval, 0, content.length); + // IServer.getLogger().log ("copied "+retval.length+ " bytes"); + return retval; + } + + public String getText () { + if (content != null) { + if (getContentType ().startsWith ("text/")) { + return new String (content); + } else { + return null; + } + } + return null; + } + + + public String getHref (INode root, INode userroot, String tmpname, String prefix) { + return prefix + getUrl (root, userroot, tmpname); + } + + + /** + * Get the path to eiter the general data-root or the user root, depending on + * where this node is located. + */ + public String getUrl (INode root, INode userroot, String tmpname) { + // String fullname = ""; + String divider = "/"; + StringBuffer b = new StringBuffer (); + INode p = this; + int loopWatch = 0; + while (p != null && p.getParent () != null && p != root && p != userroot) { + b.insert (0, divider); + b.insert (0, UrlEncoder.encode (p.getNameOrID ())); + + if ("user".equals (p.getPrototype ())) { + b.insert (0, "users"+divider); + break; + } + p = p.getParent (); + + if (loopWatch++ > 10) + break; + } + return b.toString()+UrlEncoder.encode (tmpname); + } + public long lastModified () { return lastmodified; } @@ -1689,106 +1748,63 @@ public final class Node implements INode, Serializable { return "HopObject " + name; } - /** - * Tell whether this node is stored inside a relational db. This doesn't mean - * 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 (); - } + /** + * Recursively convert other implementations of INode into helma.objectmodel.db.Node. + */ + protected Node convert (INode n) { + Hashtable ntable = new Hashtable (); + Node converted = new Node (n, ntable, false, nmgr); + return converted; + } /** * 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) + 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 (), false); + if (next != null && next.getType () == IProperty.NODE) { + Node n = (Node) next.getNodeValue (); + if (n != null && n.state == TRANSIENT) n.makePersistentCapable (); } - for (Enumeration e = properties (); e.hasMoreElements (); ) { - IProperty next = get ((String) e.nextElement (), false); - if (next != null && next.getType () == IProperty.NODE) { - Node n = (Node) next.getNodeValue (); - if (n != null && n.state == TRANSIENT) - n.makePersistentCapable (); - } - } } + state = NEW; } /** - * Get the cache node for this node. This can be - * used to store transient cache data per node from Javascript. + * 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(); + cacheNode = new helma.objectmodel.Node(); return cacheNode; } - /** - * Reset the cache node for this node. - */ - 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 () { + // walk down node path to the first non-virtual node and return its id. + // limit max depth to 3, since there shouldn't be more then 2 layers of virtual nodes. + public String getNonVirtualHomeID () { INode node = this; for (int i=0; i<3; i++) { if (node == null) break; if (node.getState() != Node.VIRTUAL) - return node; + return node.getID (); 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; - } - - /** - * We overwrite hashCode to make it dependant from the prototype. That way, when the prototype - * 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 void dump () { - System.err.println ("subnodes: "+subnodes); - System.err.println ("properties: "+propMap); - System.err.println ("links: "+links); + public void dumpSubnodes () { + System.err.println (subnodes); } } - diff --git a/src/helma/objectmodel/db/NodeHandle.java b/src/helma/objectmodel/db/NodeHandle.java deleted file mode 100644 index 3908d0e3..00000000 --- a/src/helma/objectmodel/db/NodeHandle.java +++ /dev/null @@ -1,119 +0,0 @@ -// NodeHandle.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - -package helma.objectmodel.db; - -import helma.objectmodel.*; -import java.io.Serializable; - -/** - * This class is a handle or reference to a Node. This is to abstract from different - * methods of reference: Transient Nodes are referred to directly, while persistent - * nodes are referred to via key/node manager. - * - * A handle is used to refer to a node in a safe way over a longer period. - * While a direct reference may point to a node that has been evicted from the cache - * and reinstanciated since being set, NodeHandle will always return an up-to-date - * instance of its node. - */ - -public final class NodeHandle implements INodeState, Serializable { - - // direct reference to the node - private Node node; - - // the node's key - private Key key; - - // cached DbMapping - private transient DbMapping dbmap; - - 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 (); - } - } - - /** - * Builds a handle given a node's retrieval information. At the time this is called, - * 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; - } - - /** - * Get the node described by this node handle - */ - public Node getNode (WrappedNodeManager nodemgr) { - if (node != null) - return node; - return nodemgr.getNode (key); - } - - /** - * 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; - } - - /** - * 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 (); - } - - 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; - } - } - - /** - * 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+"]"; - } - -} - diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index 0021e732..c1e754b9 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -3,13 +3,16 @@ package helma.objectmodel.db; +import java.io.*; +import java.util.Vector; +import java.util.Properties; import helma.util.CacheMap; import helma.objectmodel.*; import helma.framework.core.Application; import com.sleepycat.db.*; import java.sql.*; -import java.io.*; -import java.util.*; +import java.util.Vector; +import java.util.Enumeration; import com.workingdogs.village.*; /** @@ -20,12 +23,10 @@ import com.workingdogs.village.*; public final class NodeManager { - protected Application app; + private Application app; private CacheMap cache; - private Replicator replicator; - protected DbWrapper db; protected IDGenerator idgen; @@ -37,29 +38,15 @@ public final class NodeManager { // 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 DbException { 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 ("set up node cache ("+cacheSize+")"); + IServer.getLogger().log ("set up node cache ("+cacheSize+")"); safe = new WrappedNodeManager (this); - // nullNode = new Node (); - - String replicationUrl = props.getProperty ("replicationUrl"); - if (replicationUrl != null) { - replicator = new Replicator (); - replicator.addUrl (replicationUrl); - } else - replicator = null; // get the initial id generator value String idb = props.getProperty ("idBaseValue"); @@ -68,7 +55,7 @@ public final class NodeManager { idBaseValue = Math.max (1l, idBaseValue); // 0 and 1 are reserved for root nodes } catch (NumberFormatException ignore) {} - db = new DbWrapper (dbHome, helma.main.Server.dbFilename, this, helma.main.Server.useTransactions); + db = new DbWrapper (dbHome, Server.dbFilename, Server.useTransactions); initDb (); logSql = "true".equalsIgnoreCase(props.getProperty ("logsql")); @@ -103,7 +90,6 @@ public final class NodeManager { node.nmgr = safe; } catch (ObjectNotFoundException notfound) { node = new Node ("root", "0", "root", safe); - node.setDbMapping (app.getDbMapping ("root")); db.save (txn, node.getID (), node); registerNode (node); // register node with nodemanager cache } @@ -113,7 +99,6 @@ public final class NodeManager { node.nmgr = safe; } catch (ObjectNotFoundException notfound) { node = new Node ("users", "1", null, safe); - node.setDbMapping (app.getDbMapping ("__userroot__")); db.save (txn, node.getID (), node); registerNode (node); // register node with nodemanager cache } @@ -130,23 +115,11 @@ public final class NodeManager { } - /** - * Shut down this node manager. This is called when the application using this - * node manager is stopped. - */ public void shutdown () throws DbException { db.shutdown (); - if (cache != null) { - synchronized (cache) { - cache.clear (); - cache = null; - } - } + this.cache = null; } - /** - * Delete a node from the database. - */ public void deleteNode (Node node) throws Exception { if (node != null) { String id = node.getID (); @@ -159,16 +132,16 @@ public final class NodeManager { } - /** - * Get a node by key. - */ - public Node getNode (Key key) throws Exception { + public Node getNode (String kstr, DbMapping dbmap) throws Exception { + + if (kstr == null) + return null; 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); + Key key = new Key (dbmap, kstr); // See if Transactor has already come across this node Node node = tx.getVisitedNode (key); @@ -184,22 +157,11 @@ public final class NodeManager { // 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); - + node = getNodeByKey (db, tx.txn, kstr, dbmap); if (node != null) { synchronized (cache) { Node oldnode = (Node) cache.put (node.getKey (), node); - if (oldnode != null && !oldnode.isNullNode() && oldnode.getState () != Node.INVALID) { + if (oldnode != null && oldnode.getState () != Node.INVALID) { cache.put (node.getKey (), oldnode); node = oldnode; } @@ -216,9 +178,6 @@ public final class NodeManager { } - /** - * Get a node by relation, using the home node, the relation and a key to apply. - */ public Node getNode (Node home, String kstr, Relation rel) throws Exception { if (kstr == null) @@ -227,16 +186,13 @@ public final class NodeManager { 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); + // If what we want is a virtual node create a "synthetic" key + if (rel.virtual || rel.groupby != null) + key = new Key ((String) null, home.getKey ().getVirtualID (kstr)); + // if a key for a node from within the DB 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); - + key = new Key (rel.other, rel.getKeyID (home, kstr)); + // See if Transactor has already come across this node Node node = tx.getVisitedNode (key); @@ -244,36 +200,19 @@ public final class NodeManager { // 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. + // 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 && !rel.virtual && !rel.usesPrimaryKey ()) { - // check if node is null node (cached null) - if (node.isNullNode ()) { - if (node.created() < rel.otherType.getLastDataChange ()) - node = null; // cached null not valid anymore - // apply different consistency checks for groupby nodes and database nodes: - // for group nodes, check if they're contained - } else if (rel.groupby != null) { - if (home.contains (node) < 0) - node = null; - // for database nodes, check if constraints are fulfilled - } else 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); + + node = getNodeByRelation (db, tx.txn, home, kstr, rel); if (node != null) { @@ -283,28 +222,15 @@ public final class NodeManager { // 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) { + if (oldnode != null && oldnode.getState () != Node.INVALID) { cache.put (primKey, oldnode); - if (!keyIsPrimary) { + if (!keyIsPrimary) cache.put (key, oldnode); - } node = oldnode; - } else if (!keyIsPrimary) { - // cache node with secondary key + } 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 ()) { @@ -327,17 +253,12 @@ public final class NodeManager { 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 ()); @@ -356,10 +277,6 @@ public final class NodeManager { //////////////////////////////////////////////////////////////////////// - /** - * Insert a new node in the embedded database or a relational database table, depending - * on its db mapping. - */ public void insertNode (DbWrapper db, DbTxn txn, Node node) throws Exception { Transactor tx = (Transactor) Thread.currentThread (); @@ -370,7 +287,7 @@ public final class NodeManager { if (dbm == null || !dbm.isRelational ()) { db.save (txn, node.getID (), node); } else { - app.logEvent ("inserting relational node: "+node.getID ()); + IServer.getLogger().log ("inserting relational node: "+node.getID ()); TableDataSet tds = null; try { tds = new TableDataSet (dbm.getConnection (), dbm.getSchema (), dbm.getKeyDef ()); @@ -381,11 +298,10 @@ public final class NodeManager { if (nameField != null) rec.setValue (nameField, node.getName ()); - for (Iterator i=dbm.getProp2DB().entrySet().iterator(); i.hasNext(); ) { - Map.Entry e = (Map.Entry) i.next (); - String propname = (String) e.getKey (); - Relation rel = (Relation) e.getValue (); + for (Enumeration e=dbm.prop2db.keys(); e.hasMoreElements(); ) { + String propname = (String) e.nextElement (); Property p = node.getProperty (propname, false); + Relation rel = dbm.propertyToColumnName (propname); if (p != null && rel != null) { switch (p.getType ()) { @@ -406,7 +322,7 @@ public final class NodeManager { rec.setValue (rel.getDbField(), p.getFloatValue ()); break; case IProperty.NODE: - if (rel.reftype == Relation.REFERENCE) { + if (rel.direction == Relation.FORWARD) { // INode n = p.getNodeValue (); // String foreignID = n == null ? null : n.getID (); rec.setValue (rel.getDbField(), p.getStringValue ()); @@ -418,26 +334,18 @@ public final class NodeManager { rec.setValueNull (rel.getDbField()); } } - - if (dbm.getPrototypeField () != null) { - rec.setValue (dbm.getPrototypeField (), node.getPrototype ()); - } rec.markForInsert (); tds.save (); } finally { - if (tds != null) try { + if (tds != null) { tds.close (); - } catch (Exception ignore) {} + } } - dbm.notifyDataChange (); + dbm.lastDataChange = System.currentTimeMillis (); } // tx.timer.endEvent ("insertNode "+node); } - /** - * Updates a modified node in the embedded db or an external relational database, depending - * on its database mapping. - */ public void updateNode (DbWrapper db, DbTxn txn, Node node) throws Exception { Transactor tx = (Transactor) Thread.currentThread (); @@ -457,57 +365,46 @@ public final class NodeManager { int updated = 0; - for (Iterator i=dbm.getProp2DB().entrySet().iterator(); i.hasNext(); ) { - Map.Entry e = (Map.Entry) i.next (); - String propname = (String) e.getKey (); - Relation rel = (Relation) e.getValue (); - - // skip properties that don't need to be updated before fetching them - if (rel != null && (rel.readonly || rel.virtual || - (rel.reftype != Relation.REFERENCE && rel.reftype != Relation.PRIMITIVE))) - continue; - + for (Enumeration e=dbm.prop2db.keys(); e.hasMoreElements(); ) { + String propname = (String) e.nextElement (); Property p = node.getProperty (propname, false); + Relation rel = dbm.propertyToColumnName (propname); + if (rel != null && rel.readonly) + continue; if (p != null && rel != null) { if (p.dirty) { switch (p.getType ()) { case IProperty.STRING: - updated++; rec.setValue (rel.getDbField(), p.getStringValue ()); break; case IProperty.BOOLEAN: - updated++; rec.setValue (rel.getDbField(), p.getBooleanValue ()); break; case IProperty.DATE: - updated++; Timestamp t = new Timestamp (p.getDateValue ().getTime ()); rec.setValue (rel.getDbField(), t); break; case IProperty.INTEGER: - updated++; rec.setValue (rel.getDbField(), p.getIntegerValue ()); break; case IProperty.FLOAT: - updated++; rec.setValue (rel.getDbField(), p.getFloatValue ()); break; case IProperty.NODE: - if (!rel.virtual && rel.reftype == Relation.REFERENCE) { + if (rel.direction == Relation.FORWARD) { // INode n = p.getNodeValue (); // String foreignID = n == null ? null : n.getID (); - updated++; rec.setValue (rel.getDbField(), p.getStringValue ()); } break; } + updated++; p.dirty = false; } } else if (rel != null && rel.getDbField() != null) { - updated++; rec.setValueNull (rel.getDbField()); } @@ -519,24 +416,21 @@ public final class NodeManager { tds.save (); } } finally { - if (tds != null) try { + if (tds != null) { tds.close (); - } catch (Exception ignore) {} + } } - dbm.notifyDataChange (); + dbm.lastDataChange = System.currentTimeMillis (); } // update may cause changes in the node's parent subnode array if (node.isAnonymous()) { - Node parent = node.getCachedParent (); + Node parent = (Node) node.getParent (); if (parent != null) parent.lastSubnodeChange = System.currentTimeMillis (); } // tx.timer.endEvent ("updateNode "+node); } - /** - * Performs the actual deletion of a node from either the embedded or an external SQL database. - */ public void deleteNode (DbWrapper db, DbTxn txn, Node node) throws Exception { Transactor tx = (Transactor) Thread.currentThread (); @@ -553,11 +447,10 @@ public final class NodeManager { st = con.createStatement (); st.executeUpdate ("DELETE FROM "+dbm.getTableName ()+" WHERE "+dbm.getIDField ()+" = "+node.getID ()); } finally { - if (st != null) try { + if (st != null) st.close (); - } catch (Exception ignore) {} } - dbm.notifyDataChange (); + dbm.lastDataChange = System.currentTimeMillis (); } // node may still be cached via non-primary keys. mark as invalid node.setState (Node.INVALID); @@ -579,28 +472,20 @@ public final class NodeManager { String q = "SELECT MAX("+map.getIDField()+") FROM "+map.getTableName(); qds = new QueryDataSet (con, q); qds.fetchRecords (); - // check for empty table - if (qds.size () == 0) { - long currMax = map.getNewID (0); - retval = Long.toString (currMax); - } else { - long currMax = qds.getRecord (0).getValue (1).asLong (); - currMax = map.getNewID (currMax); - retval = Long.toString (currMax); - } + long currMax = qds.getRecord (0).getValue (1).asLong (); + currMax = Math.max (currMax+1, map.lastID+1); + map.lastID = currMax; + retval = Long.toString (currMax); } finally { // tx.timer.endEvent ("generateID "+map); - if (qds != null) try { + if (qds != null) { qds.close (); - } catch (Exception ignore) {} + } } return retval; } - /** - * Generate a new ID from an Oracle sequence. - */ public String generateID (DbMapping map) throws Exception { Transactor tx = (Transactor) Thread.currentThread (); @@ -613,12 +498,12 @@ public final class NodeManager { String q = "SELECT "+map.getIDgen()+".nextval FROM dual"; qds = new QueryDataSet (con, q); qds.fetchRecords (); - retval = qds.getRecord (0).getValue (1).asString (); + retval = qds.getRecord (0).getValue (1).asString (); } finally { // tx.timer.endEvent ("generateID "+map); - if (qds != null) try { + if (qds != null) { qds.close (); - } catch (Exception ignore) {} + } } return retval; } @@ -628,69 +513,59 @@ public final class NodeManager { * Loades subnodes via subnode relation. Only the ID index is loaded, the nodes are * loaded later on demand. */ - public List getNodeIDs (Node home, Relation rel) throws Exception { + public Vector getNodeIDs (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 ()) { + if (rel == null || rel.other == null || !rel.other.isRelational ()) { // this should never be called for embedded nodes - throw new RuntimeException ("NodeMgr.getNodeIDs called for non-relational node "+home); + throw new RuntimeException ("NodeMgr.countNodes called for non-relational node "+home); } else { - List retval = new ArrayList (); + 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 idfield = rel.groupby == null ? rel.otherType.getIDField () : rel.groupby; - Connection con = rel.otherType.getConnection (); - String table = rel.otherType.getTableName (); + String idfield = rel.groupby == null ? rel.other.getIDField () : rel.groupby; + Connection con = rel.other.getConnection (); + String table = rel.other.getTableName (); - Statement stmt = null; + QueryDataSet qds = null; try { - - String q = null; - + Relation subrel = rel; + if (subrel.getFilter () != null) + subrel = subrel.getFilter (); + if (home.getSubnodeRelation() != null) { // subnode relation was explicitly set - q = "SELECT "+idfield+" FROM "+table+" "+home.getSubnodeRelation(); + qds = new QueryDataSet (con, "SELECT "+idfield+" FROM "+table+" "+home.getSubnodeRelation()); } else { - // let relation object build the query - q = "SELECT "+idfield+" FROM "+table + rel.buildQuery (home, home.getNonVirtualParent (), null, " WHERE ", true); + String q = "SELECT "+idfield+" FROM "+table; + if (subrel.direction == Relation.BACKWARD) { + String homeid = home.getNonVirtualHomeID (); // home.getState() == Node.VIRTUAL ? home.parentID : home.getID (); + q += " WHERE "+subrel.getRemoteField()+" = '"+homeid+"'"; + } + // set order, if specified and if not using subnode's relation + if (rel.groupby != null) + q += " GROUP BY "+rel.groupby+" ORDER BY "+(rel.groupbyorder == null ? rel.groupby : rel.groupbyorder); + else if (rel.order != null) + q += " ORDER BY "+rel.order; + qds = new QueryDataSet (con, q); } if (logSql) - app.logEvent ("### getNodeIDs: "+q); + IServer.getLogger().log ("### getNodeIDs: "+qds.getSelectString()); - stmt = con.createStatement (); - if (rel.maxSize > 0) - stmt.setMaxRows (rel.maxSize); - ResultSet result = stmt.executeQuery (q); - - // 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); - } + qds.fetchRecords (); + for (int i=0; i 0) - tds.fetchRecords (rel.maxSize); - else - tds.fetchRecords (); - + if (logSql) + IServer.getLogger().log ("### getNodes: "+tds.getSelectString()); + + tds.fetchRecords (); for (int i=0; i 0 ? Math.min (rel.maxSize, retval) : retval; + return retval; } } @@ -822,16 +694,16 @@ public final class NodeManager { Transactor tx = (Transactor) Thread.currentThread (); // tx.timer.beginEvent ("getNodeIDs "+home); - if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) { + if (rel == null || rel.other == null || !rel.other.isRelational ()) { // this should never be called for embedded nodes - throw new RuntimeException ("NodeMgr.getPropertyNames called for non-relational node "+home); + throw new RuntimeException ("NodeMgr.countNodes 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.accessor; - Connection con = rel.otherType.getConnection (); - String table = rel.otherType.getTableName (); + String namefield = rel.getRemoteField (); + Connection con = rel.other.getConnection (); + String table = rel.other.getTableName (); QueryDataSet qds = null; @@ -840,7 +712,7 @@ public final class NodeManager { qds = new QueryDataSet (con, q); if (logSql) - app.logEvent ("### getPropertyNames: "+qds.getSelectString()); + IServer.getLogger().log ("### getPropertyNames: "+qds.getSelectString()); qds.fetchRecords (); for (int i=0; i 5) { + where.append (" and "); + where.append (nodesubrel.trim().substring(5).trim()); + } else { + Relation subrel = home.getDbMapping().getSubnodeRelation (); + if (subrel != null) { + if (subrel.getFilter () != null) + subrel = subrel.getFilter (); + if (subrel != null && subrel.direction == Relation.BACKWARD) { + where.append (" and "); + where.append (subrel.getRemoteField()); + where.append (" = '"); + where.append (homeid); + where.append ("'"); + } + } + } + } - if (home.getSubnodeRelation () != null) { - // combine our key with the constraints in the manually set subnode relation - StringBuffer where = new StringBuffer (); - where.append (rel.accessor); - where.append (" = '"); - where.append (escape(kstr)); - where.append ("' AND "); - where.append (home.getSubnodeRelation ().trim().substring(5).trim()); - tds.where (where.toString ()); - } else - tds.where (rel.buildQuery (home, home.getNonVirtualParent (), kstr, "", false)); + tds.where (where.toString ()); if (logSql) - app.logEvent ("### getNodeByRelation: "+tds.getSelectString()); + IServer.getLogger().log ("### getNodeByRelation: "+tds.getSelectString()); tds.fetchRecords (); @@ -962,7 +867,7 @@ public final class NodeManager { if (tds.size () > 1) throw new RuntimeException ("More than one value returned by query."); Record rec = tds.getRecord (0); - node = new Node (rel.otherType, rec, safe); + node = new Node (rel.other, rec, safe); // Check if node is already cached with primary Key. if (!rel.usesPrimaryKey()) { @@ -974,9 +879,9 @@ public final class NodeManager { } } finally { - if (tds != null) try { + if (tds != null) { tds.close (); - } catch (Exception ignore) {} + } } } return node; @@ -984,83 +889,183 @@ public final class NodeManager { // a utility method to escape single quotes private String escape (String str) { - if (str == null) - return null; - if (str.indexOf ("'") < 0) - return str; - int l = str.length(); + 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; - - 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); - } - - 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 b7da69c1..f1392d10 100644 --- a/src/helma/objectmodel/db/Property.java +++ b/src/helma/objectmodel/db/Property.java @@ -23,8 +23,8 @@ public final class Property implements IProperty, Serializable, Cloneable { protected boolean bvalue; protected long lvalue; protected double dvalue; - // protected String nvalueID; - protected NodeHandle nhandle; + protected String nvalueID; + private transient DbMapping dbm; protected Object jvalue; protected int type; @@ -40,11 +40,7 @@ public final class Property implements IProperty, Serializable, Cloneable { type = in.readInt (); switch (type) { case STRING: - // try to convert from old format - if (node.version < 7) - svalue = in.readUTF (); - else - svalue = (String) in.readObject (); + svalue = in.readUTF (); break; case BOOLEAN: bvalue = in.readBoolean (); @@ -57,11 +53,7 @@ public final class Property implements IProperty, Serializable, Cloneable { dvalue = in.readDouble (); break; case NODE: - // try to convert from old format - if (node.version > 4) - nhandle = (NodeHandle) in.readObject (); - else - nhandle = new NodeHandle (new DbKey (null, in.readUTF ())); + nvalueID = in.readUTF (); break; case JAVAOBJECT: jvalue = in.readObject (); @@ -73,12 +65,15 @@ public final class Property implements IProperty, Serializable, Cloneable { } private void writeObject (ObjectOutputStream out) throws IOException { + // don't even start if this is a non-serializable Java object + if (type == JAVAOBJECT && jvalue != null && !(jvalue instanceof Serializable)) + return; out.writeUTF (propname); out.writeObject (node); out.writeInt (type); switch (type) { case STRING: - out.writeObject (svalue); + out.writeUTF (svalue); break; case BOOLEAN: out.writeBoolean (bvalue); @@ -91,13 +86,10 @@ public final class Property implements IProperty, Serializable, Cloneable { out.writeDouble (dvalue); break; case NODE: - out.writeObject (nhandle); + out.writeUTF (nvalueID); break; case JAVAOBJECT: - if (jvalue != null && !(jvalue instanceof Serializable)) - out.writeObject (null); - else - out.writeObject (jvalue); + out.writeObject (jvalue); break; } } @@ -117,7 +109,7 @@ public final class Property implements IProperty, Serializable, Cloneable { public Property (String propname, Node node, Node value) { this (propname, node); type = NODE; - nhandle = value == null ? null : value.getHandle (); + nvalueID = value == null ? null : value.getID (); dirty = true; } @@ -148,11 +140,41 @@ public final class Property implements IProperty, Serializable, Cloneable { public void setStringValue (String value) { if (type == NODE) unregisterNode (); + // IServer.getLogger().log ("setting string value of property "+propname + " to "+value); + // mark property as dirty + dirty = true; + // if this is not a string property, try to parse a value out of it + if (type == DATE) { + SimpleDateFormat dateformat = new SimpleDateFormat (); + try { + dateformat.setLenient (true); + Date date = dateformat.parse (value); + this.lvalue = date.getTime (); + return; + } catch (ParseException nodate) { + IServer.getLogger().log ("Couldn't parse date: was expecting something like "+dateformat.format (new Date())); + // store as plain string + } + } + if (type == BOOLEAN) { + if ("true".equalsIgnoreCase (value)) + this.bvalue = true; + else if ("false".equalsIgnoreCase (value)) + this.bvalue = false; + return; + } + if (type == INTEGER) { + this.lvalue = Long.parseLong (value); + return; + } + if (type == FLOAT) { + this.dvalue = new Double (value).doubleValue (); + return; + } if (type == JAVAOBJECT) this.jvalue = null; - type = STRING; this.svalue = value; - dirty = true; + type = STRING; } @@ -197,16 +219,39 @@ public final class Property implements IProperty, Serializable, Cloneable { } public void setNodeValue (Node value) { - // value.checkWriteLock (); + value.checkWriteLock (); if (type == NODE) unregisterNode (); if (type == JAVAOBJECT) this.jvalue = null; - - // registerNode (value); + registerNode (value); type = NODE; - - nhandle = value.getHandle (); + if (node.dbmap != null) { + Relation rel = node.dbmap.getPropertyRelation (propname); + if (rel != null && rel.other != null) { + DbMapping vmap = value.getDbMapping (); + // check if actual type matches expected type + if (rel.other != vmap && (!rel.virtual || rel.prototype != null)) { + throw new RuntimeException ("Can't assign property: expected prototype "+rel.other+", got "+vmap); + } + // check if this is a forward relation, i.e. if we point to a field in the value object + // if so, we may use something else than the object's id to refer to it. + if (!rel.virtual && rel.direction == Relation.FORWARD) { + if (rel.usesPrimaryKey ()) { + this.nvalueID = value.getID (); + } else try { + this.nvalueID = value.getString (vmap.columnNameToProperty (rel.getRemoteField()).propname, false); + } catch (Exception x) { + throw new RuntimeException ("Can't set "+propname+" to "+value+": error retrieving target property"); + } + this.dbm = null; + dirty = true; + return; + } + } + } + this.nvalueID = value == null ? null : value.getID (); + this.dbm = value == null ? null : value.getDbMapping (); dirty = true; } @@ -223,39 +268,48 @@ public final class Property implements IProperty, Serializable, Cloneable { * If this was the "main" property for the node, also remove all other references. */ protected void unregisterNode () { - Node nvalue = null; - if (nhandle != null) - nvalue = nhandle.getNode (node.nmgr); - - DbMapping nvmap = null; - Relation nvrel = null; - 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.subnodesAreProperties) { - 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 (nvalueID != null) { + DbMapping nvmap = null; + Relation nvrel = null; + if (node.dbmap != null) { + nvmap = node.dbmap.getPropertyMapping (propname); + nvrel = node.dbmap.getPropertyRelation (propname); + } + Node nvalue = node.nmgr.getNode (nvalueID, nvmap); + 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.subnodesAreProperties) { + 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 (); + } else { + nvalue.unregisterPropLink (this.node); + } } } } + /** + * Tell the value node that it is being used as a property value. + */ + protected void registerNode (Node n) { + // only need to call registerPropLink if the value node is not stored in a relational db + if (n != null && (n.dbmap == null || !n.dbmap.isRelational())) { + n.registerPropLink (this.node); + } + } + public String getStringValue () { switch (type) { case STRING: @@ -270,9 +324,9 @@ public final class Property implements IProperty, Serializable, Cloneable { case FLOAT: return Double.toString (dvalue); case NODE: - return nhandle.getID (); + return nvalueID; case JAVAOBJECT: - return jvalue == null ? null : jvalue.toString (); + return jvalue.toString (); } return ""; } @@ -307,10 +361,46 @@ public final class Property implements IProperty, Serializable, Cloneable { } public INode getNodeValue () { - - if (nhandle != null) { - Node n = nhandle.getNode (node.nmgr); - if (n != null) return n; + + if (type == NODE && nvalueID != null) { + Relation rel = null; + if (dbm == null && node.dbmap != null) { + // try to get DbMap for property, if it isn't known yet + rel = node.dbmap.getPropertyRelation (propname); + // figure out db mapping from relation + if (rel != null) { + // is the property a virtual node containing objects from relational db? + if (rel.virtual && rel.other.isRelational ()) + return node.nmgr.getNode (node, propname, rel); + else if (!rel.virtual && rel.direction == Relation.FORWARD) + return node.nmgr.getNode (node, nvalueID, rel); + // avoid setting dbm for virtual and groupby relations, except for + // [mountpoint] kind of prototyped virtual nodes + else if ((!rel.virtual || rel.prototype != null) && rel.groupby == null) + dbm = rel.other; + } + } + + // we have what we need, now get the node from the node manager + Node retval = node.nmgr.getNode (nvalueID, dbm); + if (retval != null && retval.parentID == null && !"root".equalsIgnoreCase (retval.getPrototype ())) { + retval.setParent (node); + retval.setName (propname); + retval.anonymous = false; + } + + if (retval != null && retval.getDbMapping () == null && rel != null && rel.virtual && rel.prototype == null) { + // a virtual node whose child nodes are not relational - + // set up dbmapping that describes subnodes and properties + DbMapping _dbm = new DbMapping (); + _dbm.setSubnodeMapping (rel.other); + _dbm.setPropertyMapping (rel.other); + _dbm.setSubnodeRelation (rel.getVirtualSubnodeRelation()); + _dbm.setPropertyRelation (rel.getVirtualPropertyRelation()); + retval.setDbMapping (_dbm); + } + + return retval; } return null; } @@ -321,9 +411,91 @@ public final class Property implements IProperty, Serializable, Cloneable { return null; } + public String getEditor () { + switch (type) { + case STRING: + return "password".equalsIgnoreCase (propname) ? + "" : + "" ; + case BOOLEAN: + return ""; + case INTEGER: + return "" ; + case FLOAT: + return "" ; + case DATE: + SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm"); + String date = format.format (new Date (lvalue)); + return ""; + case NODE: + DbMapping nvmap = null; + if (node.dbmap != null) + nvmap = node.dbmap.getPropertyMapping (propname); + return ""; + } + return ""; + } + + private String escape (String s) { + char c[] = new char[s.length()]; + s.getChars (0, c.length, c, 0); + StringBuffer b = new StringBuffer (); + int copyfrom = 0; + for (int i = 0; i < c.length; i++) { + switch (c[i]) { + case '\\': + case '"': + if (i-copyfrom > 0) + b.append (c, copyfrom, i-copyfrom); + b.append ('\\'); + b.append (c[i]); + copyfrom = i+1; + } + } + if (c.length-copyfrom > 0) + b.append (c, copyfrom, c.length-copyfrom); + return b.toString (); + } public int getType () { return type; + + } + + public String getTypeString () { + switch (type) { + case STRING: + return "string"; + case BOOLEAN: + return "boolean"; + case DATE: + return "date"; + case INTEGER: + return "integer"; + case FLOAT: + return "float"; + case NODE: + return "node"; + } + return ""; + } + + + public Object clone () { + try { + Property c = (Property) super.clone(); + c.propname = this.propname; + c.svalue = this.svalue; + c.bvalue = this.bvalue; + c.lvalue = this.lvalue; + c.dvalue = this.dvalue; + c.nvalueID = this.nvalueID; + c.type = this.type; + return c; + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError (); + } } } diff --git a/src/helma/objectmodel/db/Relation.java b/src/helma/objectmodel/db/Relation.java deleted file mode 100644 index 03814497..00000000 --- a/src/helma/objectmodel/db/Relation.java +++ /dev/null @@ -1,773 +0,0 @@ -// Relation.java -// Copyright (c) Hannes Wallnöfer 1997-2000 - -package helma.objectmodel.db; - -import helma.objectmodel.*; -import helma.framework.core.Application; -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 - * relational database table. This can be either a scalar property (string, date, number etc.) - * or a reference to one or more other objects. - */ -public 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 - 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 - 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; - - // if this relation defines a virtual node, we need to provide a DbMapping for these virtual nodes - DbMapping virtualMapping; - - Relation virtualRelation; - Relation groupRelation; - - String propName; - String columnName; - - int reftype; - - Constraint[] constraints; - - boolean virtual; - boolean readonly; - boolean aggressiveLoading; - boolean aggressiveCaching; - boolean subnodesAreProperties; - String accessor; // db column used to access objects through this relation - String order; - String groupbyorder; - String groupby; - String prototype; - String groupbyprototype; - String filter; - int maxSize = 0; - - // Relation subnoderelation = null; // additional relation used to filter subnodes for virtual nodes - - /** - * 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.accessor = rel.accessor; - this.maxSize = rel.maxSize; - this.subnodesAreProperties = rel.subnodesAreProperties; - } - - /** - * 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 void update (String desc, Properties props, int version) { - if (version == 0) - update_v0 (desc, props); - else if (version == 1) - update_v1 (desc, props); - } - - //////////////////////////////////////////////////////////////////////////////////////////// - // parse methods for file format v0 - //////////////////////////////////////////////////////////////////////////////////////////// - - private void update_v0 (String desc, Properties props) { - boolean mountpoint = false; - Vector cnst = null; - - if (desc == null || "".equals (desc.trim ())) { - if (propName != null) { - reftype = PRIMITIVE; - columnName = propName; - } else { - reftype = INVALID; - columnName = propName; - } - } else { - desc = desc.trim (); - String descLower = desc.toLowerCase (); - if (descLower.startsWith ("[virtual]")) { - desc = desc.substring (9).trim (); - virtual = true; - } else if (descLower.startsWith ("[collection]")) { - desc = desc.substring (12).trim (); - virtual = true; - } else if (descLower.startsWith ("[mountpoint]")) { - desc = desc.substring (12).trim (); - virtual = true; - mountpoint = true; - } else { - virtual = false; - } - if (descLower.startsWith ("[readonly]")) { - desc = desc.substring (10).trim (); - readonly = true; - } else { - readonly = false; - } - } - - // parse the basic properties of this mapping - parseMapping_v0 (desc, mountpoint); - - // the following options only apply to object relations - if (reftype != PRIMITIVE && reftype != INVALID) { - - cnst = new Vector (); - - Constraint c = parseConstraint_v0 (desc); - - if (c != null) - cnst.add (c); - - parseOptions_v0 (cnst, props); - - constraints = new Constraint[cnst.size()]; - cnst.copyInto (constraints); - } - } - - /** - * Parse a line describing a mapping of a property field. If the mapping is a - * object reference of a collection of objects, put any constraints in the Vector. - */ - protected void parseMapping_v0 (String desc, boolean mountpoint) { - - Application app = ownType.getApplication (); - - if (desc.indexOf ("<") > -1) { - reftype = COLLECTION; - int lt = desc.indexOf ("<"); - int dot = desc.indexOf ("."); - String other = dot < 0 ? desc.substring (lt+1).trim () : desc.substring (lt+1, dot).trim (); - otherType = app.getDbMapping (other); - if (otherType == null) - throw new RuntimeException ("DbMapping for "+other+" not found from "+ownType.typename); - columnName = null; - if (mountpoint) - prototype = other; - } else if (desc.indexOf (">") > -1) { - reftype = REFERENCE; - int bt = desc.indexOf (">"); - int dot = desc.indexOf ("."); - String other = dot > -1 ? desc.substring (bt+1, dot).trim () : desc.substring (bt+1).trim (); - otherType = app.getDbMapping (other); - if (otherType == null) - throw new RuntimeException ("DbMapping for "+other+" not found from "+ownType.typename); - columnName = desc.substring (0, bt).trim (); - if (mountpoint) - prototype = other; - } else if (desc.indexOf (".") > -1) { - reftype = COLLECTION; - int dot = desc.indexOf ("."); - String other = desc.substring (0, dot).trim (); - otherType = app.getDbMapping (other); - if (otherType == null) - throw new RuntimeException ("DbMapping for "+other+" not found from "+ownType.typename); - columnName = null; - // set accessor - accessor = desc.substring (dot+1).trim (); - if (mountpoint) - prototype = other; - } else { - if (virtual) { - reftype = COLLECTION; - otherType = app.getDbMapping (desc); - if (otherType == null) - throw new RuntimeException ("DbMapping for "+desc+" not found from "+ownType.typename); - if (mountpoint) - prototype = desc; - } else { - reftype = PRIMITIVE; - columnName = desc.trim (); - } - } - } - - - /** - * Parse a line describing a mapping of a property field. If the mapping is a - * object reference of a collection of objects, put any constraints in the Vector. - */ - protected Constraint parseConstraint_v0 (String desc) { - if (desc.indexOf ("<") > -1) { - int lt = desc.indexOf ("<"); - int dot = desc.indexOf ("."); - String remoteField = dot < 0 ? null : desc.substring (dot+1).trim (); - String localField = lt <= 0 ? null : desc.substring (0, lt).trim (); - return new Constraint (localField, otherType.getTableName (), remoteField, false); - } else if (desc.indexOf (">") > -1) { - int bt = desc.indexOf (">"); - int dot = desc.indexOf ("."); - String localField = desc.substring (0, bt).trim (); - String remoteField = dot < 0 ? null : desc.substring (dot+1).trim (); - return new Constraint (localField, otherType.getTableName (), remoteField, false); - } - return null; - } - - protected void parseOptions_v0 (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; - } - // get group by property - groupby = props.getProperty (propName+".groupby"); - if (groupby != null && groupby.trim().length() == 0) - groupby = null; - if (groupby != null) { - groupbyorder = props.getProperty (propName+".groupby.order"); - if (groupbyorder != null && groupbyorder.trim().length() == 0) - groupbyorder = null; - groupbyprototype = props.getProperty (propName+".groupby.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 - if ("_properties".equalsIgnoreCase (propName) || virtual) { - String subnodes2props = props.getProperty (propName+".aresubnodes"); - subnodesAreProperties = "true".equalsIgnoreCase (subnodes2props); - if (virtual) { - String subnodefilter = props.getProperty (propName+".subnoderelation"); - if (subnodefilter != null) { - Constraint c = parseConstraint_v0 (subnodefilter); - if (c != null) { - cnst.add (c); - } - } - } - // update virtual mapping, if it already exists - if (virtualMapping != null) { - virtualMapping.subnodesRel = getVirtualSubnodeRelation (); - virtualMapping.propertiesRel = getVirtualPropertyRelation (); - virtualMapping.lastTypeChange = ownType.lastTypeChange; - } - } - } - - //////////////////////////////////////////////////////////////////////////////////////////// - // parse methods for file format v1 - //////////////////////////////////////////////////////////////////////////////////////////// - - private void update_v1 (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 (desc+".readonly"); - if (rdonly != null && "true".equalsIgnoreCase (rdonly)) { - readonly = true; - } else { - readonly = false; - } - } - // the following options only apply to object and collection relations - if (reftype != PRIMITIVE && reftype != INVALID) { - - Vector newConstraints = new Vector (); - parseOptions_v1 (newConstraints, props); - - constraints = new Constraint[newConstraints.size()]; - newConstraints.copyInto (constraints); - } - } - - - protected void parseOptions_v1 (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; - } - // 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 - accessor = props.getProperty (propName+".accessname"); - if (accessor != null) - subnodesAreProperties = true; - // 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; - } - - /** - * Tell if this relation represents a primitive (scalar) value mapping. - */ - public boolean isPrimitive () { - return reftype == PRIMITIVE; - } - - /** - * Return the prototype to be used for object reached by this relation - */ - public String getPrototype () { - return prototype; - } - - /** - * Return the name of the local property this relation is defined for - */ - public String getPropName () { - return propName; - } - - - /** - * 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; - } - } - - - public boolean usesPrimaryKey () { - if (otherType != null) { - if (reftype == REFERENCE) - return constraints.length == 1 && constraints[0].foreignKeyIsPrimary (); - if (reftype == COLLECTION) - return accessor == null || accessor.equalsIgnoreCase (otherType.getIDField ()); - } - return false; - } - - public String getAccessor () { - return accessor; - } - - public Relation getSubnodeRelation () { - // return subnoderelation; - return null; - } - - - /** - * Return the local field name for updates. - */ - public String getDbField () { - return columnName; - } - - - public DbMapping getVirtualMapping () { - if (!virtual) - return null; - if (virtualMapping == null) { - virtualMapping = new DbMapping (ownType.app); - virtualMapping.subnodesRel = getVirtualSubnodeRelation (); - virtualMapping.propertiesRel = 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.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.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)); - 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; - } - - - /** - * 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 = accessor == null ? otherType.getIDField () : accessor; - q.append (accessColumn); - q.append (" = "); - // check if column is string type and value needs to be quoted - if (otherType.isStringColumn (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"+otherType+"]" + c; - } - - /** - * The Constraint class represents a part of the where clause in the query used to - * establish a relation between database mapped objects. - */ - class Constraint { - - 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; - } - - 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, false); - } - q.append (foreignName); - q.append (" = "); - if (otherType.isStringColumn (foreignName)) { - q.append ("'"); - q.append (escape (local)); - q.append ("'"); - } else - q.append (escape (local)); - } - - 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 deleted file mode 100644 index a949704c..00000000 --- a/src/helma/objectmodel/db/Replicator.java +++ /dev/null @@ -1,158 +0,0 @@ -// Replicator.java -// Copyright (c) Hannes Wallnöfer 2001 - -package helma.objectmodel.db; - -import helma.framework.IReplicatedApp; -import java.rmi.*; -import java.util.*; - -/** - * This class replicates the updates of transactions to other applications via RMI - */ - -public class Replicator implements Runnable { - - Vector urls; - Vector apps; - Vector add, delete, currentAdd, currentDelete; - Thread runner; - - public Replicator () { - urls = new Vector (); - apps = new Vector (); - add = new Vector (); - delete = new Vector (); - runner = new Thread (this); - runner.start (); - } - - public void addUrl (String url) { - urls.addElement (url); - } - - public void addApp (IReplicatedApp app) { - apps.addElement (app); - } - - public void run () { - while (Thread.currentThread () == runner) { - if (prepareReplication ()) { - for (int i=0; i 0) + p.put ("java.library.path", libpath+System.getProperty("path.separator")+libdir.getCanonicalPath()); + else + p.put ("java.library.path", libdir.getCanonicalPath()); + + 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()); + + dbSources = new Hashtable (); + + new Server (); + + } + + public Server () { + + try { + checkRunning (); // check if a server is already running with this db + } catch (Exception running) { + System.out.println (running.getMessage ()); + System.exit (1); + } + + // nmgr = new NodeManager (this, sysProps); + + mainThread = new Thread (this); + mainThread.start (); + } + + public void run () { + + try { + + // set up dbSources + try { + dbProps = new SystemProperties (dbPropfile); + String sources = dbProps.getProperty ("sources", ""); + StringTokenizer st = new StringTokenizer (sources, ",; "); + String next = null; + while (st.hasMoreTokens ()) try { + next = st.nextToken (); + new DbSource (next); + } catch (Exception wrong) { + getLogger().log ("Error creating DbSource "+next); + getLogger().log ("Reason: "+wrong); + } + } catch (Exception x) { + getLogger().log ("Error loading data source properties: "+x); + } + + // start embedded web server if port is specified + if (webport > 0) { + websrv = new Acme.Serve.Serve (webport, sysProps); + } + + String xmlparser = sysProps.getProperty ("xmlparser"); + if (xmlparser != null) + XmlRpc.setDriver (xmlparser); + // XmlRpc.setDebug (true); + xmlrpc = new WebServer (port+1); + 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 "+(port+1)); + + // the following seems not to be necessary after all ... + // System.setSecurityManager(new RMISecurityManager()); + if (paranoid) { + HopSocketFactory factory = new HopSocketFactory (); + String rallow = sysProps.getProperty ("allowWeb"); + if (rallow != null) { + StringTokenizer st = new StringTokenizer (rallow, " ,;"); + while (st.hasMoreTokens ()) + factory.addAddress (st.nextToken ()); + } + RMISocketFactory.setSocketFactory (factory); + } + + if (websrv == null) { + getLogger().log ("Starting server on port "+port); + LocateRegistry.createRegistry (port); + } + + + // start application framework + String appDir = sysProps.getProperty ("apphome", "apps"); + File appHome = new File (appDir); + if (hopHome != null && !appHome.isAbsolute()) + appHome = new File (hopHome, appDir); + appsProps = new SystemProperties (appsPropfile); + File dbHome = new File (dbDir); + appManager = new ApplicationManager (port, appHome, dbHome, appsProps, this); + + + } catch (Exception gx) { + getLogger().log ("Error initializing embedded database: "+gx); + gx.printStackTrace (); + /* try { + transactor.abort (); + } catch (Exception ignore) {} */ + return; + } + + // start applications + appManager.startAll (); + + // start embedded web server + if (websrv != null) { + Thread webthread = new Thread (websrv, "WebServer"); + webthread.start (); + } + + 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); + } + } + + } + + + private void checkRunning () throws Exception { + try { + java.net.Socket socket = new java.net.Socket ("localhost", port); + } 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"); } } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/helma/objectmodel/db/SyntheticKey.java b/src/helma/objectmodel/db/SyntheticKey.java deleted file mode 100644 index 7f1911ac..00000000 --- a/src/helma/objectmodel/db/SyntheticKey.java +++ /dev/null @@ -1,123 +0,0 @@ -// SyntheticKey.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - -package helma.objectmodel.db; - -import java.io.Serializable; - -/** - * This is the internal key for an object that is not - or not directly - fetched from a db, - * but derived from another object. This is useful for all kinds of object accessed via a - * symbolic name from another object, like objects mounted via a property name column, - * virtual nodes and groupby nodes. - */ -public final class SyntheticKey implements Key, Serializable { - - private final Key parentKey; - private final String name; - - // lazily initialized hashcode - private transient int hashcode = 0; - - - /** - * make a key for a persistent Object, describing its datasource and id. - */ - public SyntheticKey (Key key, String name) { - this.parentKey = key; - this.name = name; - } - - - public boolean equals (Object what) { - if (what == this) - return true; - try { - SyntheticKey k = (SyntheticKey) what; - return parentKey.equals (k.parentKey) && - (name == k.name || name.equals (k.name)); - } catch (Exception x) { - return false; - } - } - - public int hashCode () { - if (hashcode == 0) - hashcode = 17 + 37*name.hashCode () + 37*parentKey.hashCode (); - return hashcode; - } - - - public Key getParentKey () { - return parentKey; - } - - public String getID () { - return name; - } - - public String getStorageName () { - return null; - } - - public String toString () { - return parentKey+"/"+name; - } - - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/objectmodel/db/Transactor.java b/src/helma/objectmodel/db/Transactor.java index 66d2c3d9..f2a587b6 100644 --- a/src/helma/objectmodel/db/Transactor.java +++ b/src/helma/objectmodel/db/Transactor.java @@ -22,7 +22,6 @@ public class Transactor extends Thread { // List of nodes to be updated private HashMap nodes; - private ArrayList nodesArray; // List of visited clean nodes private HashMap cleannodes; // Is a transaction in progress? @@ -45,7 +44,6 @@ public class Transactor extends Thread { super (group, runnable, group.getName ()); this.nmgr = nmgr; nodes = new HashMap (); - nodesArray = new ArrayList (); cleannodes = new HashMap (); sqlCon = new HashMap (); active = false; @@ -58,7 +56,6 @@ public class Transactor extends Thread { Key key = node.getKey (); if (!nodes.containsKey (key)) { nodes.put (key, node); - nodesArray.add (node); } } } @@ -67,7 +64,6 @@ public class Transactor extends Thread { if (node != null) { Key key = node.getKey (); nodes.remove (key); - nodesArray.remove (node); } } @@ -114,7 +110,6 @@ public class Transactor extends Thread { abort (); nodes.clear (); - nodesArray.clear (); cleannodes.clear (); txn = nmgr.db.beginTransaction (); active = true; @@ -130,44 +125,36 @@ public class Transactor extends Thread { } int ins = 0, upd = 0, dlt = 0; - int l = nodesArray.size (); + int l = nodes.size (); - Replicator replicator = nmgr.getReplicator (); + for (Iterator i=nodes.values().iterator(); i.hasNext (); ) { + Node node = (Node) i.next (); - for (int i=0; i append to StringBuffer - if ( childNode.getNodeType()==org.w3c.dom.Node.TEXT_NODE ) { - textcontent.append( childNode.getNodeValue().trim() ); - continue; - } - - // it's some kind of element (property or child) - if ( childNode.getNodeType()==org.w3c.dom.Node.ELEMENT_NODE ) { - - Element childElement = (Element)childNode; - - // get the basic key we have to look for in the properties-table - domKey = (element.getNodeName()+"."+childElement.getNodeName()).toLowerCase(); - - // is there a childtext-2-property mapping? - if ( props!=null && props.containsKey(domKey+"._text") ) { - helmaKey = (String)props.get(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 ); - if ( helmaNode.getString(helmaKey,false)==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 = (String)props.get(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); - if ( helmaNode.getNode(helmaKey,false)==null ) { - convert( childElement, helmaNode.createNode(helmaKey) ); - if (DEBUG) debug( "read " + childElement.toString() + helmaNode.getNode(helmaKey,false).toString() ); - } - continue; - } - - - // - // map it to one of the children-lists - helma.objectmodel.INode newHelmaNode = null; - String childrenMapping = (String)props.get(element.getNodeName().toLowerCase()+"._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) ); - } - // which name to choose for a virtual subnode: - helmaKey = (String)props.get(domKey); - if ( helmaKey==null ) { - helmaKey = childElement.getNodeName().replace(':',defaultSeparator); - } - // try to get the virtual node - helma.objectmodel.INode worknode = helmaNode.getNode( helmaKey, false ); - 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 ) ); - } - } - // forget about other types (comments etc) - continue; - } - - // if there's some text content for this element, map it: - if ( textcontent.length()>0 ) { - helmaKey = (String)props.get(element.getNodeName().toLowerCase()+"._text"); - if ( helmaKey==null ) - helmaKey = "text"; - helmaNode.setString(helmaKey, textcontent.toString().trim() ); - } - - return helmaNode; - } - - /** - * set element's attributes as properties of helmaNode - */ - private INode attributes( Element element, INode helmaNode ) { - NamedNodeMap nnm = element.getAttributes(); - int len = nnm.getLength(); - for ( int i=0; i"); - writeln (""); - writeln ("" ); - write (""); - write (node,null,0); - writeln (""); - convertedNodes = null; - return true; - } - - /** - * write a hopobject and print all its properties and children. - * if node has already been fully printed, just make a reference here. - */ - public void write (INode node, String name, int level) throws IOException { - if ( ++level>maxLevels ) - return; - prefix.append(indent); - if ( convertedNodes.contains(node) ) { - writeReferenceTag (node, name); - } else { - convertedNodes.addElement (node); - writeTagOpen (node,name); - writeProperties (node,level); - writeChildren (node,level); - writeTagClose (node,name); - } - prefix = prefix.delete( prefix.length()-indent.length(), Integer.MAX_VALUE ); - } - - /** - * loop through properties and print them with their property-name - * as elementname - */ - private void writeProperties (INode node, int level) throws IOException { - Enumeration e = node.properties(); - while ( e.hasMoreElements() ) { - String key = (String)e.nextElement(); - IProperty prop = node.get(key,false); - if ( prop!=null ) { - int type = prop.getType(); - if( type==IProperty.NODE ) { - write (node.getNode(key,false), key, level); - } else { - writeProperty (node.get(key,false)); - } - } - } - } - - public void writeNullProperty (String key) throws IOException { - write (prefix.toString()); - write (indent); - write ("<"); - write (key); - write (" hop:type=\"null\"/>"); - write (LINESEPARATOR); - } - - /** - * write a single property, set attribute type according to type, - * apply xml-encoding. - */ - public void writeProperty (IProperty property) throws IOException { - write (prefix.toString()); - write (indent); - write ("<"); - write (property.getName()); - switch (property.getType()) { - case IProperty.BOOLEAN: - write (" hop:type=\"boolean\""); - break; - case IProperty.FLOAT: - write (" hop:type=\"float\""); - break; - case IProperty.INTEGER: - write (" hop:type=\"integer\""); - break; - } - if ( property.getType()==IProperty.DATE ) { - write (" hop:type=\"date\""); - SimpleDateFormat format = new SimpleDateFormat ( DATEFORMAT ); - write (">"); - write ( format.format (property.getDateValue()) ); - } else { - write (">"); - write ( HtmlEncoder.encodeXml (property.getStringValue()) ); - } - write (""); - write (LINESEPARATOR); - } - - /** - * loop through the children-array and print them as - */ - private void writeChildren (INode node, int level) throws IOException { - Enumeration e = node.getSubnodes(); - while (e.hasMoreElements()) { - INode nextNode = (INode)e.nextElement(); - write (nextNode, "hop:child", 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) throws IOException { - write (prefix.toString()); - write ("<"); - write ( (name==null)?"hopobject" : name); - write (" hop:id=\""); - write (getNodeIdentifier(node)); - write ("\" hop:prototype=\""); - write (getNodePrototype(node)); - write ("\""); - write (">"); - write (LINESEPARATOR); - } - - /** - * write a closing tag for a node - * e.g. - */ - public void writeTagClose (INode node, String name) throws IOException { - write (prefix.toString()); - write (""); - write (LINESEPARATOR); - } - - /** - * write a tag holding a reference to an element that has - * been dumped before. - * e.g. - */ - public void writeReferenceTag (INode node, String name) throws IOException { - write (prefix.toString()); - write ("<"); - write ( (name==null)?"hopobject" : name); - write ( " hop:idref=\""); - write (getNodeIdentifier(node)); - write ("\" hop:prototyperef=\""); - write (getNodePrototype(node)); - write ("\""); - 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(); - } - } - - public void writeln(String str) throws IOException { - write (str); - write (LINESEPARATOR); - } - -} - diff --git a/src/helma/scripting/ActionFile.java b/src/helma/scripting/ActionFile.java deleted file mode 100644 index 4bceaf60..00000000 --- a/src/helma/scripting/ActionFile.java +++ /dev/null @@ -1,128 +0,0 @@ -// ActionFile.java -// Copyright (c) Helma.org 1998-2002 - -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; - - -/** - * An ActionFile is a file containing function code that is exposed as a URI - * of objects of this class/type. It is - * 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; - String functionName; - 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; - functionName = getName()+"_action"; - this.file = file; - this.content = null; - if (file != null) - update (); - } - - public ActionFile (String content, String name, Prototype proto) { - this.prototype = proto; - this.app = proto.getApplication (); - this.name = name; - functionName = getName()+"_action"; - this.file = null; - this.content = content; - } - - - /** - * Abstract method that must be implemented by subclasses to update evaluators with - * new content of action file. - */ - // protected abstract void update (String content) throws Exception; - - /** - * Abstract method that must be implemented by subclasses to remove - * action from evaluators. - */ - // protected abstract void remove (); - - - /** - * 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 void update () { - - if (!file.exists ()) { - // remove functions declared by this from all object prototypes - remove (); - } else { - try { - FileReader reader = new FileReader (file); - char cbuf[] = new char[(int) file.length ()]; - reader.read (cbuf); - reader.close (); - content = new String (cbuf); - // update (content); - } catch (Exception filex) { - app.logEvent ("*** Error reading action file "+file+": "+filex); - } - lastmod = file.lastModified (); - } - } - - protected void remove () { - prototype.actions.remove (name); - if (file != null) - prototype.updatables.remove (file.getName()); - } - - public String getName () { - return name; - } - - public String getContent () { - return content; - } - - public String getFunctionName () { - return functionName; - } - - public Prototype getPrototype () { - return prototype; - } - - public Application getApplication () { - return app; - } - - public String toString () { - return "ActionFile["+prototype.getName()+"/"+functionName+"]"; - } - -} - - diff --git a/src/helma/scripting/FunctionFile.java b/src/helma/scripting/FunctionFile.java deleted file mode 100644 index 85076c18..00000000 --- a/src/helma/scripting/FunctionFile.java +++ /dev/null @@ -1,153 +0,0 @@ -// FunctionFile.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - -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; - - -/** - * This represents a File containing script functions for a given class/prototype. - */ - - -public class FunctionFile implements Updatable { - - String name; - Prototype prototype; - Application app; - File file; - String content; - long lastmod; - - // a set of funcion names defined by this file. We keep this to be able to - // remove them once the file should get removed - HashSet declaredProps; - long declaredPropsTimestamp; - - public FunctionFile (File file, String name, Prototype proto) { - this.prototype = proto; - this.app = proto.getApplication (); - this.name = name; - this.file = file; - update (); - } - - /** - * Create a function file without a file, passing the code directly. This is used for - * files contained in zipped applications. The whole update mechanism is bypassed - * by immediately parsing the code. - */ - public FunctionFile (String body, String name, Prototype proto) { - this.prototype = proto; - this.app = proto.getApplication (); - this.name = name; - this.file = null; - this.content = body; - update (); - } - - /** - * 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 void update () { - - if (file != null) { - if (!file.exists ()) { - remove (); - } else { - lastmod = file.lastModified (); - // app.typemgr.readFunctionFile (file, prototype.getName ()); - // app.getScriptingEnvironment().evaluateFile (prototype, file); - } - } else { - // app.getScriptingEnvironment().evaluateString (prototype, content); - } - } - - /* public void evaluate (ScriptingEnvironment env) { - if (file != null) - env.evaluateFile (prototype, file); - else - env.evaluateString (prototype, content); - }*/ - public boolean hasFile () { - return file != null; - } - - public File getFile () { - return file; - } - - public String getContent () { - return content; - } - - - void remove () { - prototype.functions.remove (name); - prototype.updatables.remove (file.getName()); - - // if we did not add anything to any evaluator, we're done - /* if (declaredProps == null || declaredProps.size() == 0) - return; - - removeProperties (declaredProps); */ - } - - /** - * Remove the properties in the HashMap iff they're still the same as declared by this file. - * This method is called by remove() with the latest props, and by update with the prior props - * after the file has been reevaluated. - */ - void removeProperties (HashSet props) { - // first loop through other function files in this prototype to make a set of properties - // owned by other files. -/* HashSet otherFiles = new HashSet (); - for (Iterator it=prototype.functions.values ().iterator (); it.hasNext (); ) { - FunctionFile other = (FunctionFile) it.next (); - if (other != this && other.declaredProps != null) - otherFiles.addAll (other.declaredProps); - } - - Iterator evals = app.typemgr.getRegisteredRequestEvaluators (); - while (evals.hasNext ()) { - try { - RequestEvaluator reval = (RequestEvaluator) evals.next (); - ObjectPrototype op = reval.getPrototype (prototype.getName()); - for (Iterator it = props.iterator (); it.hasNext (); ) { - String fname = (String) it.next (); - // check if this property has been declared by some other function file in the meantime - if (otherFiles.contains (fname)) - continue; - op.deleteProperty (fname, fname.hashCode()); - // System.err.println ("REMOVING PROP: "+fname); - } - } catch (Exception ignore) {} - } */ - } - - public String toString () { - if (file == null) - return "[Zipped script file]"; - else - return prototype.getName()+"/"+file.getName(); - } - - -} - - diff --git a/src/helma/scripting/ScriptingEnvironment.java b/src/helma/scripting/ScriptingEnvironment.java deleted file mode 100644 index 4ded3664..00000000 --- a/src/helma/scripting/ScriptingEnvironment.java +++ /dev/null @@ -1,75 +0,0 @@ -// ScriptingEnvironment.java -// Copyright (c) Hannes Wallnöfer 1998-2001 - -package helma.scripting; - -import helma.framework.core.Application; -import helma.framework.core.Prototype; -import helma.framework.core.RequestEvaluator; -import java.util.*; -import java.io.File; - -/** - * This is the interface that must be implemented to make a scripting environment - * usable by the Helma application server. - */ -public interface ScriptingEnvironment { - - /** - * Initialize the environment using the given properties - */ - public void init (Application app, Properties props) throws ScriptingException; - - /** - * A prototype has been updated and must be re-evaluated. - */ - public void updatePrototype (Prototype prototype); - - /** - * Invoke a function on some object, using the given arguments and global vars. - */ - public Object invoke (Object thisObject, String functionName, Object[] args, - HashMap globals, RequestEvaluator reval) - throws ScriptingException; - - /** - * Get a property on an object - */ - public Object get (Object thisObject, String key, RequestEvaluator reval); - - /** - * Return true if a function by that name is defined for that object. - */ - public boolean hasFunction (Object thisObject, String functionName, RequestEvaluator reval) - throws ScriptingException; - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/scripting/ScriptingException.java b/src/helma/scripting/ScriptingException.java deleted file mode 100644 index d738013b..00000000 --- a/src/helma/scripting/ScriptingException.java +++ /dev/null @@ -1,52 +0,0 @@ -// ScriptingException.java -// Copyright (c) Hannes Wallnöfer 1998-2001 - -package helma.scripting; - - -/** - * The base class for exceptions thrown by Helma scripting package - */ -public class ScriptingException extends Exception { - - /** - * Construct a ScriptingException given an error message - */ - public ScriptingException (String msg) { - super (msg); - } - - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/scripting/fesi/ESGenericObject.java b/src/helma/scripting/fesi/ESGenericObject.java deleted file mode 100644 index c396bf74..00000000 --- a/src/helma/scripting/fesi/ESGenericObject.java +++ /dev/null @@ -1,109 +0,0 @@ -// ESGenericObject.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - - -package helma.scripting.fesi; - -import helma.framework.core.*; -import helma.framework.IPathElement; -import FESI.Interpreter.*; -import FESI.Exceptions.*; -import FESI.Data.*; -import java.util.*; - - -/** - * 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; - } - - /** - * 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) - return false; - if (what == this) - return true; - if (what instanceof ESGenericObject) { - ESGenericObject other = (ESGenericObject) what; - return (wrappedObject.equals (other.wrappedObject)); - } - return false; - } - -} - - diff --git a/src/helma/scripting/fesi/ESMapWrapper.java b/src/helma/scripting/fesi/ESMapWrapper.java deleted file mode 100644 index dd1402e6..00000000 --- a/src/helma/scripting/fesi/ESMapWrapper.java +++ /dev/null @@ -1,156 +0,0 @@ -// ESMapWrapper.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - - -package helma.scripting.fesi; - -import helma.framework.core.*; -import helma.objectmodel.INode; -import FESI.Data.*; -import FESI.Exceptions.*; -import FESI.Interpreter.Evaluator; -import java.util.*; - - - -/** - * An EcmaScript object that makes stuff in a hashtable accessible as its properties - */ - -public class ESMapWrapper extends ESWrapper { - - private Map data; - private FesiEvaluator fesi; - - public ESMapWrapper (FesiEvaluator fesi) { - super (new Object(), fesi.getEvaluator ()); - this.fesi = fesi; - } - - public ESMapWrapper (FesiEvaluator fesi, Map data) { - super (new Object(), fesi.getEvaluator ()); - this.fesi = fesi; - this.data = 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 (); - data.put (propertyName, propertyValue); - } - - public boolean deleteProperty(String propertyName, int hash) throws EcmaScriptException { - data.remove (propertyName); - return true; - } - - 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 fesi.getNodeWrapper ((INode) val); - else if (val instanceof ESValue) - return (ESValue) val; - return ESLoader.normalizeValue(val, evaluator); - } - - - public Enumeration getAllProperties () { - return getProperties (); - } - - 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++]; - } - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/scripting/fesi/ESUser.java b/src/helma/scripting/fesi/ESUser.java deleted file mode 100644 index 57819880..00000000 --- a/src/helma/scripting/fesi/ESUser.java +++ /dev/null @@ -1,121 +0,0 @@ -// ESUser.java -// Copyright (c) Hannes Wallnöfer 1998-2000 - -package helma.scripting.fesi; - -import helma.framework.core.*; -import helma.objectmodel.*; -import helma.objectmodel.db.Node; -import FESI.Interpreter.*; -import FESI.Exceptions.*; -import FESI.Data.*; - -/** - * The ESUser is a special kind of Node object that represents a user of - * a Helma application. The actual user session data are kept in class User. - * If the user is logged in as a registered member, the wrapped node represents - * the user object in the database, while for anonymous surfers the node object - * is just a transient node.

- * This means that the wrapped node will be swapped when the user logs in or out. - * To save session state across logins and logouts, the - * cache property of the user object stays the same for the whole time the user - * spends on this site. - */ - -public class ESUser extends ESNode { - - /** if the user is online, this is his/her online session object */ - public User user; - - public ESUser (INode node, FesiEvaluator eval, User user) { - super (eval.getPrototype("user"), eval.getEvaluator(), node, eval); - this.user = user; - if (user != null) { - cache = user.getCache (); - cacheWrapper = new ESNode (cache, eval); - } - } - - /** - * Overrides getProperty to return the uid (which is not a regular property) - */ - public ESValue getProperty (String propname, int hash) throws EcmaScriptException { - // if there is a user session object, we expose some of its properties. - // Otherwise, we call the parent's class getProperty method. - if ("uid".equals (propname)) { - if (user == null || user.getUID () == null) - return ESNull.theNull; - else - return new ESString (user.getUID ()); - } - if ("sessionID".equals (propname)) { - if (user == null || user.getSessionID () == null) - return ESNull.theNull; - else - return new ESString (user.getSessionID ()); - } - // if this represents an active user object, we override - // the cache property to come from the user session object - // instead of the Node object. - if ("cache".equals (propname) && user != null) { - cache = user.getCache (); - cacheWrapper.node = cache; - return cacheWrapper; - } - return super.getProperty (propname, hash); - } - - - /** - * The node for a user object changes at login and logout, so we don't use our - * own node, but just reach through to the session user object instead. - */ - public void setNode (INode node) { - // this only makes sense if this wrapper represents an active user - if (user == null) - return; - // set the node on the transient user session object - user.setNode (node); - if (node != null) { - this.node = node; - } else { - // user.getNode will never return null. If the node is set to null (=user logged out) - // it will user the original transient cache node again. - this.node = user.getNode (); - } - // set node handle to wrapped node - if (node instanceof Node) - handle = ((Node) node).getHandle (); - else - handle = null; - // we don't take over the transient cache from the node, - // because we always stick to the one from the user object. - } - - public void updateNodeFromUser () { - // this only makes sense if this wrapper represents an active user - if (user == null) - return; - node = user.getNode (); - // set node handle to wrapped node - if (node instanceof Node) - handle = ((Node) node).getHandle (); - else - handle = null; - - } - - public boolean clearCache () { - if (user != null) - user.clearCache (); - else - super.clearCache (); - return true; - } - - public String toString () { - return ("UserObject "+node.getName ()); - } - -} - diff --git a/src/helma/scripting/fesi/FesiEvaluator.java b/src/helma/scripting/fesi/FesiEvaluator.java deleted file mode 100644 index 0097f0ec..00000000 --- a/src/helma/scripting/fesi/FesiEvaluator.java +++ /dev/null @@ -1,587 +0,0 @@ -// FesiScriptingEnvironment.java -// Copyright (c) Hannes Wallnöfer 2002 - -package helma.scripting.fesi; - -import helma.scripting.*; -import helma.scripting.fesi.extensions.*; -import helma.framework.*; -import helma.framework.core.*; -import helma.objectmodel.*; -import helma.objectmodel.db.DbMapping; -import helma.objectmodel.db.Relation; -import helma.util.Updatable; -import java.util.*; -import java.io.*; -import FESI.Data.*; -import FESI.Interpreter.*; -import FESI.Exceptions.*; -import Acme.LruHashtable; - -/** - * This is the implementation of ScriptingEnvironment for the FESI EcmaScript interpreter. - */ - -public class FesiEvaluator { - - // the application we're running in - public Application app; - - // The FESI evaluator - Evaluator evaluator; - - // the global object - GlobalObject global; - - // caching table for JavaScript object wrappers - LruHashtable wrappercache; - - // table containing JavaScript prototypes - Hashtable prototypes; - - // the request evaluator instance owning this fesi evaluator - RequestEvaluator reval; - - // extensions loaded by this evaluator - static String[] extensions = new String[] { - "FESI.Extensions.BasicIO", - "FESI.Extensions.FileIO", - "helma.xmlrpc.fesi.FesiRpcExtension", - "helma.scripting.fesi.extensions.ImageExtension", - "helma.scripting.fesi.extensions.FtpExtension", - "FESI.Extensions.JavaAccess", - "FESI.Extensions.OptionalRegExp"}; - - public FesiEvaluator (Application app, RequestEvaluator reval) { - this.app = app; - this.reval = reval; - wrappercache = new LruHashtable (100, .80f); - prototypes = new Hashtable (); - try { - evaluator = new Evaluator(); - evaluator.reval = this; - global = evaluator.getGlobalObject(); - for (int i=0; i "+esv.getClass()); - if (esv instanceof ConstructedFunctionObject || esv instanceof FesiActionAdapter.ThrowException) - op.deleteProperty (prop, prop.hashCode()); - } catch (Exception x) {} - } - } - - - - /** - * Invoke a function on some object, using the given arguments and global vars. - */ - public Object invoke (Object thisObject, String functionName, Object[] args, HashMap globals) throws ScriptingException { - ESObject eso = null; - if (thisObject == null) - eso = global; - else - eso = getElementWrapper (thisObject); - - GlobalObject global = evaluator.getGlobalObject (); - - // if we are provided with global variables to set for this invocation, - // remember the global variables before invocation to be able to reset them afterwards. - Set globalVariables = null; - try { - ESValue[] esv = args == null ? new ESValue[0] : new ESValue[args.length]; - for (int i=0; i 0 && n.getDbMapping () == null) { - n.setDbMapping (app.getDbMapping (protoname)); - } - - op = getPrototype (protoname); - - // no prototype found for this node? - if (op == null) - op = getPrototype("hopobject"); - - - DbMapping dbm = n.getDbMapping (); - if (dbm != null && dbm.isInstanceOf ("user")) - esn = new ESUser (n, this, null); - else - esn = new ESNode (op, evaluator, n, this); - - 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); - } - - /** - * Get a scripting wrapper object for a user object. Active user objects are represented by - * the special ESUser wrapper class. - */ - public ESNode getNodeWrapper (User u) { - if (u == null) - return null; - - ESUser esn = (ESUser) wrappercache.get (u); - - if (esn == null) { - esn = new ESUser (u.getNode(), this, u); - wrappercache.put (u, esn); - } else { - // the user node may have changed (login/logout) while the ESUser was - // lingering in the cache. - esn.updateNodeFromUser (); - } - - return esn; - } - - /** - * Return the RequestEvaluator owning and driving this FESI evaluator. - */ - public RequestEvaluator getRequestEvaluator () { - return reval; - } - - /** - * Return the Response object of the current evaluation context. Proxy method to RequestEvaluator. - */ - 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 synchronized void evaluateFile (Prototype prototype, File file) { - try { - FileReader fr = new FileReader (file); - EvaluationSource es = new FileEvaluationSource (file.getPath (), null); - updateEvaluator (prototype, fr, es); - } catch (IOException iox) { - app.logEvent ("Error updating function file: "+iox); - } - } - - public synchronized void evaluateString (Prototype prototype, String code) { - StringReader reader = new StringReader (code); - StringEvaluationSource es = new StringEvaluationSource (code, null); - updateEvaluator (prototype, reader, es); - } - - public synchronized void updateEvaluator (Prototype prototype, Reader reader, EvaluationSource source) { - - // HashMap priorProps = null; - // HashSet newProps = null; - - try { - - ObjectPrototype op = getPrototype (prototype.getName()); - - // extract all properties from prototype _before_ evaluation, so we can compare afterwards - // but only do this is declaredProps is not up to date yet - /*if (declaredPropsTimestamp != lastmod) { - priorProps = new HashMap (); - // remember properties before evaluation, so we can tell what's new afterwards - try { - for (Enumeration en=op.getAllProperties(); en.hasMoreElements(); ) { - String prop = (String) en.nextElement (); - priorProps.put (prop, op.getProperty (prop, prop.hashCode())); - } - } catch (Exception ignore) {} - } */ - - // do the update, evaluating the file - evaluator.evaluate(reader, op, source, false); - - // check what's new - /* if (declaredPropsTimestamp != lastmod) try { - newProps = new HashSet (); - for (Enumeration en=op.getAllProperties(); en.hasMoreElements(); ) { - String prop = (String) en.nextElement (); - if (priorProps.get (prop) == null || op.getProperty (prop, prop.hashCode()) != priorProps.get (prop)) - newProps.add (prop); - } - } catch (Exception ignore) {} */ - - } catch (Throwable e) { - app.logEvent ("Error parsing function file "+source+": "+e); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException ignore) {} - } - - // now remove the props that were not refreshed, and set declared props to new collection - /* if (declaredPropsTimestamp != lastmod) { - declaredPropsTimestamp = lastmod; - if (declaredProps != null) { - declaredProps.removeAll (newProps); - removeProperties (declaredProps); - } - declaredProps = newProps; - // System.err.println ("DECLAREDPROPS = "+declaredProps); - } */ - - } - } - - -} diff --git a/src/helma/scripting/fesi/FesiScriptingEnvironment.java b/src/helma/scripting/fesi/FesiScriptingEnvironment.java deleted file mode 100644 index d58da662..00000000 --- a/src/helma/scripting/fesi/FesiScriptingEnvironment.java +++ /dev/null @@ -1,85 +0,0 @@ -// FesiScriptingEnvironment.java -// Copyright (c) Hannes Wallnöfer 2002 - -package helma.scripting.fesi; - -import helma.scripting.*; -import helma.framework.core.*; -import java.util.*; -import java.io.File; -import FESI.Data.*; -import FESI.Interpreter.*; -import FESI.Exceptions.*; - -/** - * This is the implementation of ScriptingEnvironment for the FESI EcmaScript interpreter. - */ -public class FesiScriptingEnvironment implements ScriptingEnvironment { - - Application app; - Properties props; - HashMap evaluators; - - /** - * Initialize the environment using the given properties - */ - public void init (Application app, Properties props) throws ScriptingException { - this.app = app; - this.props = props; - evaluators = new HashMap (); - } - - - /** - * A prototype has been updated and must be re-evaluated. - */ - public void updatePrototype (Prototype prototype) { - for (Iterator i = evaluators.values().iterator(); i.hasNext(); ) { - FesiEvaluator fesi = (FesiEvaluator) i.next(); - fesi.evaluatePrototype (prototype); - } - } - - /** - * Invoke a function on some object, using the given arguments and global vars. - */ - public Object invoke (Object thisObject, String functionName, Object[] args, - HashMap globals, RequestEvaluator reval) - throws ScriptingException { - // check if there is already a FesiEvaluator for this RequestEvaluator. - // if not, create one. - FesiEvaluator fesi = getEvaluator (reval); - return fesi.invoke (thisObject, functionName, args, globals); - } - - /** - * Get a property on an object - */ - public Object get (Object thisObject, String key, RequestEvaluator reval) { - FesiEvaluator fesi = getEvaluator (reval); - return fesi.getProperty (thisObject, key); - } - - /** - * Return true if a function by that name is defined for that object. - */ - public boolean hasFunction (Object thisObject, String functionName, RequestEvaluator reval) - throws ScriptingException { - FesiEvaluator fesi = getEvaluator (reval); - return fesi.hasFunction (thisObject, functionName); - } - - - Collection getEvaluators () { - return evaluators.values(); - } - - FesiEvaluator getEvaluator (RequestEvaluator reval) { - FesiEvaluator fesi = (FesiEvaluator) evaluators.get (reval); - if (fesi == null) { - fesi = new FesiEvaluator (app, reval); - evaluators.put (reval, fesi); - } - return fesi; - } -} diff --git a/src/helma/scripting/fesi/extensions/DomExtension.java b/src/helma/scripting/fesi/extensions/DomExtension.java deleted file mode 100644 index 374c6c87..00000000 --- a/src/helma/scripting/fesi/extensions/DomExtension.java +++ /dev/null @@ -1,163 +0,0 @@ -package helma.scripting.fesi.extensions; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; - -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; - -public class DomExtension extends Extension { - - private transient Evaluator evaluator = null; - - public DomExtension() { - super(); - } - - 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 ("load", new XmlLoad ("load", evaluator, fp)); - globalXml.putHiddenProperty ("save", new XmlSave ("save", evaluator, fp)); - globalXml.putHiddenProperty ("create", new XmlCreate ("create", evaluator, fp)); - globalXml.putHiddenProperty ("get", new XmlGet ("get", 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"); - } - } - - class XmlSave extends BuiltinFunctionObject { - XmlSave(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("not enough arguments"); - INode node = null; - try { - node = ((ESNode)arguments[1]).getNode(); - } catch ( Exception e ) { - // we definitly need a node - throw new EcmaScriptException("argument is not an hopobject"); - } - try { - File tmpFile = new File(arguments[0].toString()+".tmp."+XmlWriter.generateID()); - XmlWriter writer = new XmlWriter (tmpFile); - boolean result = writer.write(node); - writer.close(); - File finalFile = new File(arguments[0].toString()); - tmpFile.renameTo (finalFile); - this.evaluator.reval.app.logEvent("wrote xml to " + finalFile.getAbsolutePath() ); - } catch (IOException io) { - throw new EcmaScriptException (io.toString()); - } - return ESBoolean.makeBoolean(true); - } - } - - class XmlCreate extends BuiltinFunctionObject { - XmlCreate(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"); - 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); - boolean result = writer.write(node); - writer.flush(); - return new ESString (out.toString()); - } catch (IOException io) { - throw new EcmaScriptException (io.toString()); - } - } - } - - class XmlLoad extends BuiltinFunctionObject { - XmlLoad(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("no arguments for Xml.load()"); - 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.reval.app.getWrappedNodeManager() ); - } - try { - XmlReader reader = new XmlReader (); - INode result = reader.read (arguments[0].toString(),node); - return this.evaluator.reval.getNodeWrapper (result); - } catch ( NoClassDefFoundError e ) { - throw new EcmaScriptException ("Can't load dom-capable xml parser."); - } catch ( RuntimeException f ) { - throw new EcmaScriptException (f.toString()); - } - } - } - - class XmlGet extends BuiltinFunctionObject { - 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.reval.app.getWrappedNodeManager() ); - INode result = converter.convert (arguments[0].toString(),node); - return this.evaluator.reval.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/servlet/AbstractServletClient.java b/src/helma/servlet/AbstractServletClient.java deleted file mode 100644 index 2795c6d2..00000000 --- a/src/helma/servlet/AbstractServletClient.java +++ /dev/null @@ -1,300 +0,0 @@ -// ServletClient.java -// Copyright (c) Hannes Wallnöfer, Raphael Spannocchi 1998-2000 - -/* Portierung von helma.asp.AspClient auf Servlets */ -/* Author: Raphael Spannocchi Datum: 27.11.1998 */ - -package helma.servlet; - -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.rmi.Naming; -import java.rmi.RemoteException; -import java.util.*; -import helma.framework.*; -import helma.util.*; - -/** - * This is an abstract Hop servlet adapter. This class communicates with hop applications - * via RMI. Subclasses are either one servlet per app, or one servlet that handles multiple apps - */ - -public abstract class AbstractServletClient extends HttpServlet { - - String host = null; - int port = 0; - int uploadLimit; // limit to HTTP uploads in kB - String hopUrl; - String cookieDomain; - boolean caching; - boolean debug; - - static final byte HTTP_GET = 0; - static final byte HTTP_POST = 1; - - public void init (ServletConfig init) throws ServletException { - super.init (init); - - host = init.getInitParameter ("host"); - if (host == null) host = "localhost"; - - String portstr = init.getInitParameter ("port"); - port = portstr == null ? 5055 : Integer.parseInt (portstr); - - String upstr = init.getInitParameter ("uploadLimit"); - uploadLimit = upstr == null ? 1024 : Integer.parseInt (upstr); - - cookieDomain = init.getInitParameter ("cookieDomain"); - - hopUrl = "//" + host + ":" + port + "/"; - - debug = ("true".equalsIgnoreCase (init.getInitParameter ("debug"))); - - caching = ! ("false".equalsIgnoreCase (init.getInitParameter ("caching"))); - } - - - abstract IRemoteApp getApp (String appID) throws Exception; - - abstract void invalidateApp (String appID); - - abstract String getAppID (String reqpath); - - abstract String getRequestPath (String reqpath); - - - public void doGet (HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - execute (request, response, HTTP_GET); - } - - public void doPost (HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - execute (request, response, HTTP_POST); - } - - - protected void execute (HttpServletRequest request, HttpServletResponse response, byte method) { - String protocol = request.getProtocol (); - Cookie[] cookies = request.getCookies(); - - // get app and path from original request path - String pathInfo = request.getPathInfo (); - String appID = getAppID (pathInfo); - RequestTrans reqtrans = new RequestTrans (method); - reqtrans.path = getRequestPath (pathInfo); - - try { - - // read and set http parameters - for (Enumeration e = request.getParameterNames(); e.hasMoreElements(); ) { - String nextKey = (String)e.nextElement(); - String[] paramValues = request.getParameterValues(nextKey); - if (paramValues != null) { - reqtrans.set (nextKey, paramValues[0]); // set to single string value - if (paramValues.length > 1) - reqtrans.set (nextKey+"_array", paramValues); // set string array - } - } - - // check for MIME file uploads - String contentType = request.getContentType(); - if (contentType != null && contentType.indexOf("multipart/form-data")==0) { - // File Upload - Uploader up; - try { - if ((up = getUpload (request)) != null) { - Hashtable upload = up.getParts (); - for (Enumeration e = upload.keys(); e.hasMoreElements(); ) { - String nextKey = (String) e.nextElement (); - Object nextPart = upload.get (nextKey); - reqtrans.set (nextKey, nextPart); - } - } - } catch (Exception upx) { - String uploadErr = upx.getMessage (); - if (uploadErr == null || uploadErr.length () == 0) - uploadErr = upx.toString (); - reqtrans.set ("uploadError", uploadErr); - } - } - - // read cookies - if (cookies != null) { - for (int i=0; i < cookies.length;i++) try { - // get Cookies - String nextKey = cookies[i].getName (); - String nextPart = cookies[i].getValue (); - if ("HopSession".equals (nextKey)) - reqtrans.session = nextPart; - else - reqtrans.set (nextKey, nextPart); - } catch (Exception badCookie) {} - } - - // check if we need to create a session id - if (reqtrans.session == null) { - reqtrans.session = Long.toString (Math.round (Math.random ()*Long.MAX_VALUE), 16); - reqtrans.session += "@"+Long.toString (System.currentTimeMillis (), 16); - Cookie c = new Cookie("HopSession", reqtrans.session); - c.setPath ("/"); - if (cookieDomain != null) - c.setDomain (cookieDomain); - response.addCookie(c); - } - - // 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); - - String remotehost = request.getRemoteAddr (); - if (remotehost != null) - reqtrans.set ("http_remotehost", remotehost); - - 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 ); - - // get RMI ref to application and execute request - IRemoteApp app = getApp (appID); - ResponseTrans restrans = null; - try { - restrans = app.execute (reqtrans); - } catch (RemoteException cnx) { - invalidateApp (appID); - app = getApp (appID); - app.ping (); - restrans = app.execute (reqtrans); - } - writeResponse (response, restrans, cookies, protocol); - - } catch (Exception x) { - invalidateApp (appID); - try { - response.setContentType ("text/html"); - Writer out = response.getWriter (); - if (debug) - out.write ("Error:
" +x); - else - out.write ("This server is temporarily unavailable. Please check back later."); - out.flush (); - } catch (Exception io_e) {} - } - } - - - void writeResponse (HttpServletResponse res, ResponseTrans trans, Cookie[] cookies, String protocol) { - - for (int i = 0; i < trans.countCookies(); i++) try { - Cookie c = new Cookie(trans.getKeyAt(i), trans.getValueAt(i)); - c.setPath ("/"); - if (cookieDomain != null) - c.setDomain (cookieDomain); - int expires = trans.getDaysAt(i); - if (expires > 0) - c.setMaxAge(expires * 60*60*24); // Cookie time to live, days -> seconds - res.addCookie(c); - } catch (Exception ign) {} - - if (trans.getRedirect () != null) { - try { - res.sendRedirect(trans.getRedirect ()); - } catch(Exception io_e) {} - - } else { - - if (!trans.cache || ! caching) { - // Disable caching of response. - if (protocol == null || !protocol.endsWith ("1.1")) - res.setHeader ("Pragma", "no-cache"); // for HTTP 1.0 - else - res.setHeader ("Cache-Control", "no-cache"); // for HTTP 1.1 - } - if ( trans.realm!=null ) - res.setHeader( "WWW-Authenticate", "Basic realm=\"" + trans.realm + "\"" ); - if (trans.status > 0) - res.setStatus (trans.status); - res.setContentLength (trans.getContentLength ()); - res.setContentType (trans.getContentType ()); - try { - OutputStream out = res.getOutputStream (); - out.write (trans.getContent ()); - out.close (); - } catch(Exception io_e) {} - } - } - - private void redirectResponse (HttpServletRequest request, HttpServletResponse res, ResponseTrans trans, String url) { - try { - res.sendRedirect(url); - } catch (Exception e) { - System.err.println ("Exception at redirect: " + e + e.getMessage()); - } - } - - - public Uploader getUpload (HttpServletRequest request) throws Exception { - int contentLength = request.getContentLength (); - BufferedInputStream in = new BufferedInputStream (request.getInputStream ()); - Uploader up = null; - try { - if (contentLength > uploadLimit*1024) { - // consume all input to make Apache happy - byte b[] = new byte[1024]; - int read = 0; - while (read > -1) - read = in.read (b, 0, 1024); - throw new RuntimeException ("Upload exceeds limit of "+uploadLimit+" kb."); - } - String contentType = request.getContentType (); - up = new Uploader(uploadLimit); - up.load (in, contentType, contentLength); - } finally { - try { in.close (); } catch (Exception ignore) {} - } - return up; - } - - - public Object getUploadPart(Uploader up, String name) { - return up.getParts().get(name); - } - - - public String getServletInfo(){ - return new String("Hop Servlet Client"); - } - - -} - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/servlet/AcmeFileServlet.java b/src/helma/servlet/AcmeFileServlet.java index 9e836f6d..d2f4f003 100644 --- a/src/helma/servlet/AcmeFileServlet.java +++ b/src/helma/servlet/AcmeFileServlet.java @@ -60,12 +60,6 @@ public class AcmeFileServlet extends FileServlet this.root = root; } - public void init (ServletConfig config) throws ServletException { - super.init (config); - // do nothing - } - - /// Services a single request from the client. // @param req the servlet request // @param req the servlet response @@ -212,4 +206,3 @@ public class AcmeFileServlet extends FileServlet } - diff --git a/src/helma/servlet/AcmeServletClient.java b/src/helma/servlet/AcmeServletClient.java index 1c294f5c..eda5d42f 100644 --- a/src/helma/servlet/AcmeServletClient.java +++ b/src/helma/servlet/AcmeServletClient.java @@ -1,4 +1,4 @@ -// AcmeServletClient.java +// ServletClient.java // Copyright (c) Hannes Wallnoefer, Raphael Spannocchi 1998-2000 /* Portierung von helma.asp.AspClient auf Servlets */ @@ -12,63 +12,96 @@ import java.io.*; import java.util.*; import helma.framework.*; import helma.framework.core.Application; +import helma.objectmodel.Node; import helma.util.Uploader; /** - * This is the Hop servlet adapter that uses the Acme servlet API clone and communicates + * This is the HOP servlet adapter that uses the Acme servlet API clone and communicates * directly with hop applications instead of using RMI. */ -public class AcmeServletClient extends HttpServlet { +public class AcmeServletClient extends HttpServlet{ private int uploadLimit; // limit to HTTP uploads in kB private Hashtable apps; private Application app; private String cookieDomain; + private boolean caching; private boolean debug; - static final byte HTTP_GET = 0; - static final byte HTTP_POST = 1; public AcmeServletClient (Application app) { this.app = app; - this.uploadLimit = 1024; // generous 1mb upload limit + this.uploadLimit = 1024; // generous 1mb upload limit } - public void init (ServletConfig config) throws ServletException { - super.init (config); - // do nothing + + public void service (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + execute (request, response); } public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - execute (request, response, HTTP_GET); + execute (request, response); } - + public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - execute (request, response, HTTP_POST); + execute (request, response); } - - private void execute (HttpServletRequest request, HttpServletResponse response, byte method) { + + + private void execute (HttpServletRequest request, HttpServletResponse response) { String protocol = request.getProtocol (); Cookie[] cookies = request.getCookies(); - try { - RequestTrans reqtrans = new RequestTrans (method); + try { + RequestTrans reqtrans = new RequestTrans (); + // HACK - sessions not fully supported in Acme.Serve + // Thats ok, we dont need the session object, just the id. + reqtrans.session = request.getRequestedSessionId(); + if (cookies != null) { + for (int i=0; i < cookies.length;i++) try { // get Cookies + String nextKey = cookies[i].getName (); + String nextPart = cookies[i].getValue (); + reqtrans.set (nextKey, nextPart); + } catch (Exception badCookie) {} + } + // get optional path info + String pathInfo = request.getServletPath (); + if (pathInfo != null) { + if (pathInfo.indexOf (app.getName()) == 1) + pathInfo = pathInfo.substring (app.getName().length()+1); + reqtrans.path = trim (pathInfo); + } else + reqtrans.path = ""; + + 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); + + String remotehost = request.getRemoteAddr (); + if (remotehost != null) + reqtrans.set ("http_remotehost", remotehost); + + String browser = request.getHeader ("User-Agent"); + if (browser != null) + reqtrans.set ("http_browser", browser); - // read and set http parameters for (Enumeration e = request.getParameterNames(); e.hasMoreElements(); ) { // Params parsen String nextKey = (String)e.nextElement(); String[] paramValues = request.getParameterValues(nextKey); - if (paramValues != null) { - reqtrans.set (nextKey, paramValues[0]); // set to single string value - if (paramValues.length > 1) - reqtrans.set (nextKey+"_array", paramValues); // set string array - } - } + String nextValue = paramValues[0]; // Only take first value + reqtrans.set (nextKey, nextValue); // generic Header, Parameter + } - // check for MIME file uploads String contentType = request.getContentType(); if (contentType != null && contentType.indexOf("multipart/form-data")==0) { // File Upload @@ -90,51 +123,6 @@ public class AcmeServletClient extends HttpServlet { } } - // HACK - sessions not fully supported in Acme.Serve - // Thats ok, we dont need the session object, just the id. - reqtrans.session = request.getRequestedSessionId(); - - // get Cookies - if (cookies != null) { - for (int i=0; i < cookies.length;i++) try { - String nextKey = cookies[i].getName (); - String nextPart = cookies[i].getValue (); - reqtrans.set (nextKey, nextPart); - } catch (Exception badCookie) {} - } - - // get optional path info - String pathInfo = request.getServletPath (); - if (pathInfo != null) { - if (pathInfo.indexOf (app.getName()) == 1) - pathInfo = pathInfo.substring (app.getName().length()+1); - reqtrans.path = trim (pathInfo); - } else - reqtrans.path = ""; - - // 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); - - String remotehost = request.getRemoteAddr (); - if (remotehost != null) - reqtrans.set ("http_remotehost", remotehost); - - 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 ); - ResponseTrans restrans = null; restrans = app.execute (reqtrans); writeResponse (response, restrans, cookies, protocol); @@ -154,38 +142,34 @@ public class AcmeServletClient extends HttpServlet { } - private void writeResponse (HttpServletResponse res, ResponseTrans trans, Cookie[] cookies, String protocol) { + private void writeResponse (HttpServletResponse res, ResponseTrans trans, Cookie[] cookies, String protocol) { for (int i = 0; i < trans.countCookies(); i++) try { - Cookie c = new Cookie(trans.getKeyAt(i), trans.getValueAt(i)); + Cookie c = new Cookie(trans.getKeyAt(i), trans.getValueAt(i)); c.setPath ("/"); if (cookieDomain != null) - c.setDomain (cookieDomain); + c.setDomain (cookieDomain); int expires = trans.getDaysAt(i); if (expires > 0) - c.setMaxAge(expires * 60*60*24); // Cookie time to live, days -> seconds - res.addCookie(c); + c.setMaxAge(expires * 60*60*24); // Cookie time to live, days -> seconds + res.addCookie(c); } catch (Exception ign) {} - if (trans.getRedirect () != null) { - try { - res.sendRedirect(trans.getRedirect ()); + if (trans.redirect != null) { + try { + res.sendRedirect(trans.redirect); } catch(Exception io_e) {} } else { - - if (!trans.cache) { + if (!trans.cache || ! caching) { // Disable caching of response. if (protocol == null || !protocol.endsWith ("1.1")) res.setHeader ("Pragma", "no-cache"); // for HTTP 1.0 else res.setHeader ("Cache-Control", "no-cache"); // for HTTP 1.1 } - if ( trans.realm!=null ) - res.setHeader( "WWW-Authenticate", "Basic realm=\"" + trans.realm + "\"" ); - if (trans.status > 0) - res.setStatus (trans.status); + res.setStatus( HttpServletResponse.SC_OK ); res.setContentLength (trans.getContentLength ()); - res.setContentType (trans.getContentType ()); + res.setContentType (trans.contentType); try { OutputStream out = res.getOutputStream (); out.write (trans.getContent ()); @@ -198,7 +182,7 @@ public class AcmeServletClient extends HttpServlet { try { res.sendRedirect(url); } catch (Exception e) { - System.err.println ("Exception in redirect: " + e + e.getMessage()); + System.err.println ("Exception bei redirect: " + e + e.getMessage()); } } @@ -226,8 +210,8 @@ public class AcmeServletClient extends HttpServlet { } - public String getServletInfo (){ - return new String("Hop ServletClient"); + public String getServletInfo(){ + return new String("Helma ServletClient"); } @@ -268,4 +252,3 @@ public class AcmeServletClient extends HttpServlet { - diff --git a/src/helma/servlet/MultiServletClient.java b/src/helma/servlet/MultiServletClient.java deleted file mode 100644 index c4e1d5b3..00000000 --- a/src/helma/servlet/MultiServletClient.java +++ /dev/null @@ -1,118 +0,0 @@ -// MultiServletClient.java -// Copyright (c) Hannes Wallnöfer 2001 - - -package helma.servlet; - -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.rmi.Naming; -import java.rmi.RemoteException; -import java.util.HashMap; -import helma.framework.IRemoteApp; - -/** - * 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 HashMap apps = null; - - public void init (ServletConfig init) throws ServletException { - super.init (init); - apps = new HashMap (); - super.init (init); - } - - IRemoteApp getApp (String appID) throws Exception { - IRemoteApp retval = (IRemoteApp) apps.get (appID); - if (retval != null) { - return retval; - } - retval = (IRemoteApp) Naming.lookup (hopUrl + appID); - apps.put (appID, retval); - return retval; - } - - void invalidateApp (String appID) { - apps.remove (appID); - } - - String getAppID (String path) { - if (path == null) - throw new RuntimeException ("Invalid request path: "+path); - - char[] val = path.toCharArray (); - int len = val.length; - int st = 0; - - // advance to start of path - while ((st < len) && (val[st] <= ' ' || val[st] == '/')) - st++; - - // eat characters of first path element - int end = st; - while (end < len && val[end] != '/' && val[end] > 20) - end++; - - return new String (val, st, end -st); - } - - String getRequestPath (String path) { - if (path == null) - return ""; - - char[] val = path.toCharArray (); - int len = val.length; - int st = 0; - - // advance to start of path - while ((st < len) && (val[st] <= ' ' || val[st] == '/')) - st++; - - // eat characters of first path element - while (st < len && val[st] != '/') - st++; - if (st < len && val[st] == '/') - st++; - - // eat away noise at end of path - while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == '/')) - len--; - - return ((st > 0) || (len < val.length)) ? new String (val, st, len-st) : path; - } - - // for testing - public static void main (String args[]) { - AbstractServletClient client = new MultiServletClient (); - // String path = "///appname/do/it/for/me///"; - String path = "appname"; - System.out.println (client.getAppID (path)); - System.out.println (client.getRequestPath (path)); - } - -} - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/servlet/ServletClient.java b/src/helma/servlet/ServletClient.java index 67604adc..ecb107be 100644 --- a/src/helma/servlet/ServletClient.java +++ b/src/helma/servlet/ServletClient.java @@ -13,75 +13,283 @@ import java.rmi.Naming; import java.rmi.RemoteException; import java.util.*; import helma.framework.*; +import helma.objectmodel.Node; import helma.util.*; /** - * This is the HOP servlet adapter. This class communicates with just - * one Hop application. + * This is the HOP servlet adapter. This class communicates with hop applications + * via RMI. */ -public class ServletClient extends AbstractServletClient { +public class ServletClient extends HttpServlet{ - private IRemoteApp app = null; + private String host = null; + private int port = 0; + private int uploadLimit; // limit to HTTP uploads in kB + private Hashtable apps; private String appName; + private String appUrl; + private String cookieDomain; + private boolean caching; + private boolean debug; - public void init (ServletConfig init) throws ServletException { - super.init (init); + + public void init (ServletConfig init) { + apps = new Hashtable(); appName = init.getInitParameter ("application"); - if (appName == null) - appName = "base"; + if (appName == null) appName = "base"; - super.init (init); + host = init.getInitParameter ("host"); + if (host == null) host = "localhost"; + + String portstr = init.getInitParameter ("port"); + port = portstr == null ? 5055 : Integer.parseInt (portstr); + + String upstr = init.getInitParameter ("uploadLimit"); + uploadLimit = upstr == null ? 500 : Integer.parseInt (upstr); + + cookieDomain = init.getInitParameter ("cookieDomain"); + + appUrl = "//" + host + ":" + port + "/"; + debug = ("true".equalsIgnoreCase (init.getInitParameter ("debug"))); + + caching = ! ("false".equalsIgnoreCase (init.getInitParameter ("caching"))); } - IRemoteApp getApp (String appID) throws Exception { - if (app != null) - return app; - app = (IRemoteApp) Naming.lookup (hopUrl + appName); - return app; + private IRemoteApp getApp (String appID) throws Exception { + IRemoteApp retval = (IRemoteApp) apps.get (appID); + if (retval != null) { + return retval; + } + retval = (IRemoteApp) Naming.lookup (appUrl + appID); + apps.put (appID, retval); + return retval; } - void invalidateApp (String appID) { - app = null; + public void doGet (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + execute (appName, request, response); + } + + public void doPost (HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + execute (appName, request, response); + } + + // not used anymore + private void get(String appID, HttpServletRequest request, HttpServletResponse response) { + } + + private void execute (String appID, HttpServletRequest request, HttpServletResponse response) { + String protocol = request.getProtocol (); + Cookie[] cookies = request.getCookies(); + try { + RequestTrans reqtrans = new RequestTrans (); + if (cookies != null) { + for (int i=0; i < cookies.length;i++) try { // get Cookies + String nextKey = cookies[i].getName (); + String nextPart = cookies[i].getValue (); + if ("HopSession".equals (nextKey)) + reqtrans.session = nextPart; + else + reqtrans.set (nextKey, nextPart); + } catch (Exception badCookie) {} + } + + // check if we need to create a session id + if (reqtrans.session == null) { + reqtrans.session = Long.toString (Math.round (Math.random ()*Long.MAX_VALUE), 16); + reqtrans.session += "@"+Long.toString (System.currentTimeMillis (), 16); + Cookie c = new Cookie("HopSession", reqtrans.session); + c.setPath ("/"); + if (cookieDomain != null) + c.setDomain (cookieDomain); + response.addCookie(c); + } + + // get optional path info + String pathInfo = request.getPathInfo (); + if (pathInfo != null) + reqtrans.path = trim (pathInfo); + else + reqtrans.path = ""; + + 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); + + String remotehost = request.getRemoteAddr (); + if (remotehost != null) + reqtrans.set ("http_remotehost", remotehost); + + String browser = request.getHeader ("User-Agent"); + if (browser != null) + reqtrans.set ("http_browser", browser); + + for (Enumeration e = request.getParameterNames(); e.hasMoreElements(); ) { + // Params parsen + String nextKey = (String)e.nextElement(); + String[] paramValues = request.getParameterValues(nextKey); + String nextValue = paramValues[0]; // Only take first value + reqtrans.set (nextKey, nextValue); // generic Header, Parameter + } + + String contentType = request.getContentType(); + if (contentType != null && contentType.indexOf("multipart/form-data")==0) { + // File Upload + Uploader up; + try { + if ((up = getUpload (uploadLimit, request)) != null) { + Hashtable upload = up.getParts (); + for (Enumeration e = upload.keys(); e.hasMoreElements(); ) { + String nextKey = (String) e.nextElement (); + Object nextPart = upload.get (nextKey); + reqtrans.set (nextKey, nextPart); + } + } + } catch (Exception upx) { + String uploadErr = upx.getMessage (); + if (uploadErr == null || uploadErr.length () == 0) + uploadErr = upx.toString (); + reqtrans.set ("uploadError", uploadErr); + } + } + + // get RMI ref to application and execute request + IRemoteApp app = getApp (appID); + ResponseTrans restrans = null; + try { + restrans = app.execute (reqtrans); + } catch (RemoteException cnx) { + apps.remove (appID); + app = getApp (appID); + app.ping (); + restrans = app.execute (reqtrans); + } + writeResponse (response, restrans, cookies, protocol); + + } catch (Exception x) { + apps.remove (appID); + try { + response.setContentType ("text/html"); + Writer out = response.getWriter (); + if (debug) + out.write ("Error:
" +x); + else + out.write ("This server is temporarily unavailable. Please check back later."); + out.flush (); + } catch (Exception io_e) {} + } } - String getAppID (String path) { - return appName; + + private void writeResponse (HttpServletResponse res, ResponseTrans trans, Cookie[] cookies, String protocol) { + + for (int i = 0; i < trans.countCookies(); i++) try { + Cookie c = new Cookie(trans.getKeyAt(i), trans.getValueAt(i)); + c.setPath ("/"); + if (cookieDomain != null) + c.setDomain (cookieDomain); + int expires = trans.getDaysAt(i); + if (expires > 0) + c.setMaxAge(expires * 60*60*24); // Cookie time to live, days -> seconds + res.addCookie(c); + } catch (Exception ign) {} + + if (trans.redirect != null) { + try { + res.sendRedirect(trans.redirect); + } catch(Exception io_e) {} + + } else { + if (!trans.cache || ! caching) { + // Disable caching of response. + if (protocol == null || !protocol.endsWith ("1.1")) + res.setHeader ("Pragma", "no-cache"); // for HTTP 1.0 + else + res.setHeader ("Cache-Control", "no-cache"); // for HTTP 1.1 + } + res.setContentLength (trans.getContentLength ()); + res.setContentType (trans.contentType); + try { + OutputStream out = res.getOutputStream (); + out.write (trans.getContent ()); + out.close (); + } catch(Exception io_e) {} + } + } + + private void redirectResponse (HttpServletRequest request, HttpServletResponse res, ResponseTrans trans, String url) { + try { + res.sendRedirect(url); + } catch (Exception e) { + System.err.println ("Exception at redirect: " + e + e.getMessage()); + } + } + + + public Uploader getUpload (HttpServletRequest request) throws Exception { + return getUpload (500, request); } - String getRequestPath (String path) { - // get request path - if (path != null) - return trim (path); - else - return ""; + public Uploader getUpload (int maxKbytes, HttpServletRequest request) throws Exception { + int contentLength = request.getContentLength (); + BufferedInputStream in = new BufferedInputStream (request.getInputStream ()); + Uploader up = null; + try { + if (contentLength > maxKbytes*1024) { + // consume all input to make Apache happy + byte b[] = new byte[1024]; + int read = 0; + while (read > -1) + read = in.read (b, 0, 1024); + throw new RuntimeException ("Upload exceeds limit of "+maxKbytes+" kb."); + } + String contentType = request.getContentType (); + up = new Uploader(maxKbytes); + up.load (in, contentType, contentLength); + } finally { + try { in.close (); } catch (Exception ignore) {} + } + return up; } - String trim (String str) { + + public Object getUploadPart(Uploader up, String name) { + return up.getParts().get(name); + } + + + public String getServletInfo(){ + return new String("Helma ServletClient"); + } + + + private String trim (String str) { + + if (str == null) + return null; char[] val = str.toCharArray (); int len = val.length; int st = 0; - while ((st < len) && (val[st] <= ' ' || val[st] == '/')) + while ((st < len) && (val[st] <= ' ' || val[st] == '/')) { st++; - - while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == '/')) + } + while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == '/')) { len--; - + } return ((st > 0) || (len < val.length)) ? new String (val, st, len-st) : str; } - // for testing - public static void main (String args[]) { - AbstractServletClient client = new ServletClient (); - String path = "///appname/do/it/for/me///"; - System.out.println (client.getAppID (path)); - System.out.println (client.getRequestPath (path)); - } - - } diff --git a/src/helma/servlet/StandaloneServletClient.java b/src/helma/servlet/StandaloneServletClient.java deleted file mode 100644 index a81b3329..00000000 --- a/src/helma/servlet/StandaloneServletClient.java +++ /dev/null @@ -1,118 +0,0 @@ -// StandaloneServletClient.java -// Copyright (c) Hannes Wallnöfer, 2001 - -package helma.servlet; - -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.util.*; -import helma.framework.*; -import helma.framework.core.Application; -import helma.objectmodel.*; -import helma.util.*; - -/** - * This is a standalone Hop servlet client, running a Hop application by itself. - */ - -public final class StandaloneServletClient extends AbstractServletClient { - - private Application app = null; - private String appName; - private String serverProps; - - - public void init (ServletConfig init) throws ServletException { - super.init (init); - appName = init.getInitParameter ("application"); - serverProps = init.getInitParameter ("serverprops"); - } - - IRemoteApp getApp (String appID) { - if (app == null) - createApp (); - return app; - } - - /** - * 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 propfile = new File (serverProps); - File hopHome = new File (propfile.getParent()); - SystemProperties sysProps = new SystemProperties (propfile.getAbsolutePath()); - app = new Application (appName, hopHome, sysProps, null); - app.init (); - app.start (); - } catch (Exception x) { - System.err.println ("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) { - System.err.println ("Error shutting down app "+app.getName()+": "); - x.printStackTrace (); - } - } - app = null; - } - - void invalidateApp (String appID) { - // app = null; - } - - String getAppID (String path) { - return appName; - } - - String getRequestPath (String path) { - // get request path - if (path != null) - return trim (path); - else - return ""; - } - - String trim (String str) { - char[] val = str.toCharArray (); - int len = val.length; - int st = 0; - - while ((st < len) && (val[st] <= ' ' || val[st] == '/')) - st++; - - while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == '/')) - len--; - - return ((st > 0) || (len < val.length)) ? new String (val, st, len-st) : str; - } - - // for testing - public static void main (String args[]) { - AbstractServletClient client = new ServletClient (); - String path = "///appname/do/it/for/me///"; - System.out.println (client.getAppID (path)); - System.out.println (client.getRequestPath (path)); - } - - - -} - - - diff --git a/src/helma/util/CacheMap.java b/src/helma/util/CacheMap.java index 97d52e93..4159403b 100644 --- a/src/helma/util/CacheMap.java +++ b/src/helma/util/CacheMap.java @@ -29,7 +29,7 @@ // Moved to helma.util to use java.util.HashMap instead of java.util.Hashtable package helma.util; -import java.util.HashMap; +import java.util.*; /// A Hashtable that expires least-recently-used objects. //

@@ -60,9 +60,6 @@ public class CacheMap { private HashMap oldTable; private HashMap newTable; - // the logger to output messages to - private Logger logger = null; - /// Constructs a new, empty hashtable with the specified initial // capacity and the specified load factor. // Unlike a plain Hashtable, an LruHashtable will never grow or @@ -187,8 +184,7 @@ public class CacheMap { else { if (newTable.size() >= eachCapacity) { // Rotate the tables. - if (logger != null) - logger.log ("Rotating Cache tables at "+newTable.size()+"/"+oldTable.size()+" (new/old)"); + helma.objectmodel.IServer.getLogger().log ("Rotating Cache tables at "+newTable.size()+"/"+oldTable.size()+" (new/old)"); oldTable = newTable; newTable = new HashMap (eachCapacity, loadFactor); } @@ -213,10 +209,6 @@ public class CacheMap { oldTable.clear (); } - /// Set the logger to use for debug and profiling output - public void setLogger (Logger log) { - this.logger = log; - } public synchronized Object[] getEntryArray () { Object[] k1 = newTable.keySet().toArray(); @@ -227,9 +219,6 @@ public class CacheMap { return k; } - public String toString () { - return newTable.toString () + oldTable.toString () + hashCode (); - } } diff --git a/src/helma/util/Crypt.java b/src/helma/util/Crypt.java deleted file mode 100644 index f2f7952a..00000000 --- a/src/helma/util/Crypt.java +++ /dev/null @@ -1,629 +0,0 @@ -/**************************************************************************** - * jcrypt.java - * - * Java-based implementation of the unix crypt command - * - * Based upon C source code written by Eric Young, eay@psych.uq.oz.au - * - ****************************************************************************/ - -package helma.util; - - -public class Crypt { - - private static final int ITERATIONS = 16; - - private static final int con_salt[] = - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0A, 0x0B, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, - 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, - 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, - 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, - 0x23, 0x24, 0x25, 0x20, 0x21, 0x22, 0x23, 0x24, - 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, - 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, - 0x3D, 0x3E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, - }; - - private static final boolean shifts2[] = - { - false, false, true, true, true, true, true, true, - false, true, true, true, true, true, true, false - }; - - private static final int skb[][] = - { - { - /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ - 0x00000000, 0x00000010, 0x20000000, 0x20000010, - 0x00010000, 0x00010010, 0x20010000, 0x20010010, - 0x00000800, 0x00000810, 0x20000800, 0x20000810, - 0x00010800, 0x00010810, 0x20010800, 0x20010810, - 0x00000020, 0x00000030, 0x20000020, 0x20000030, - 0x00010020, 0x00010030, 0x20010020, 0x20010030, - 0x00000820, 0x00000830, 0x20000820, 0x20000830, - 0x00010820, 0x00010830, 0x20010820, 0x20010830, - 0x00080000, 0x00080010, 0x20080000, 0x20080010, - 0x00090000, 0x00090010, 0x20090000, 0x20090010, - 0x00080800, 0x00080810, 0x20080800, 0x20080810, - 0x00090800, 0x00090810, 0x20090800, 0x20090810, - 0x00080020, 0x00080030, 0x20080020, 0x20080030, - 0x00090020, 0x00090030, 0x20090020, 0x20090030, - 0x00080820, 0x00080830, 0x20080820, 0x20080830, - 0x00090820, 0x00090830, 0x20090820, 0x20090830, - }, - { - /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */ - 0x00000000, 0x02000000, 0x00002000, 0x02002000, - 0x00200000, 0x02200000, 0x00202000, 0x02202000, - 0x00000004, 0x02000004, 0x00002004, 0x02002004, - 0x00200004, 0x02200004, 0x00202004, 0x02202004, - 0x00000400, 0x02000400, 0x00002400, 0x02002400, - 0x00200400, 0x02200400, 0x00202400, 0x02202400, - 0x00000404, 0x02000404, 0x00002404, 0x02002404, - 0x00200404, 0x02200404, 0x00202404, 0x02202404, - 0x10000000, 0x12000000, 0x10002000, 0x12002000, - 0x10200000, 0x12200000, 0x10202000, 0x12202000, - 0x10000004, 0x12000004, 0x10002004, 0x12002004, - 0x10200004, 0x12200004, 0x10202004, 0x12202004, - 0x10000400, 0x12000400, 0x10002400, 0x12002400, - 0x10200400, 0x12200400, 0x10202400, 0x12202400, - 0x10000404, 0x12000404, 0x10002404, 0x12002404, - 0x10200404, 0x12200404, 0x10202404, 0x12202404, - }, - { - /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */ - 0x00000000, 0x00000001, 0x00040000, 0x00040001, - 0x01000000, 0x01000001, 0x01040000, 0x01040001, - 0x00000002, 0x00000003, 0x00040002, 0x00040003, - 0x01000002, 0x01000003, 0x01040002, 0x01040003, - 0x00000200, 0x00000201, 0x00040200, 0x00040201, - 0x01000200, 0x01000201, 0x01040200, 0x01040201, - 0x00000202, 0x00000203, 0x00040202, 0x00040203, - 0x01000202, 0x01000203, 0x01040202, 0x01040203, - 0x08000000, 0x08000001, 0x08040000, 0x08040001, - 0x09000000, 0x09000001, 0x09040000, 0x09040001, - 0x08000002, 0x08000003, 0x08040002, 0x08040003, - 0x09000002, 0x09000003, 0x09040002, 0x09040003, - 0x08000200, 0x08000201, 0x08040200, 0x08040201, - 0x09000200, 0x09000201, 0x09040200, 0x09040201, - 0x08000202, 0x08000203, 0x08040202, 0x08040203, - 0x09000202, 0x09000203, 0x09040202, 0x09040203, - }, - { - /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */ - 0x00000000, 0x00100000, 0x00000100, 0x00100100, - 0x00000008, 0x00100008, 0x00000108, 0x00100108, - 0x00001000, 0x00101000, 0x00001100, 0x00101100, - 0x00001008, 0x00101008, 0x00001108, 0x00101108, - 0x04000000, 0x04100000, 0x04000100, 0x04100100, - 0x04000008, 0x04100008, 0x04000108, 0x04100108, - 0x04001000, 0x04101000, 0x04001100, 0x04101100, - 0x04001008, 0x04101008, 0x04001108, 0x04101108, - 0x00020000, 0x00120000, 0x00020100, 0x00120100, - 0x00020008, 0x00120008, 0x00020108, 0x00120108, - 0x00021000, 0x00121000, 0x00021100, 0x00121100, - 0x00021008, 0x00121008, 0x00021108, 0x00121108, - 0x04020000, 0x04120000, 0x04020100, 0x04120100, - 0x04020008, 0x04120008, 0x04020108, 0x04120108, - 0x04021000, 0x04121000, 0x04021100, 0x04121100, - 0x04021008, 0x04121008, 0x04021108, 0x04121108, - }, - { - /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */ - 0x00000000, 0x10000000, 0x00010000, 0x10010000, - 0x00000004, 0x10000004, 0x00010004, 0x10010004, - 0x20000000, 0x30000000, 0x20010000, 0x30010000, - 0x20000004, 0x30000004, 0x20010004, 0x30010004, - 0x00100000, 0x10100000, 0x00110000, 0x10110000, - 0x00100004, 0x10100004, 0x00110004, 0x10110004, - 0x20100000, 0x30100000, 0x20110000, 0x30110000, - 0x20100004, 0x30100004, 0x20110004, 0x30110004, - 0x00001000, 0x10001000, 0x00011000, 0x10011000, - 0x00001004, 0x10001004, 0x00011004, 0x10011004, - 0x20001000, 0x30001000, 0x20011000, 0x30011000, - 0x20001004, 0x30001004, 0x20011004, 0x30011004, - 0x00101000, 0x10101000, 0x00111000, 0x10111000, - 0x00101004, 0x10101004, 0x00111004, 0x10111004, - 0x20101000, 0x30101000, 0x20111000, 0x30111000, - 0x20101004, 0x30101004, 0x20111004, 0x30111004, - }, - { - /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */ - 0x00000000, 0x08000000, 0x00000008, 0x08000008, - 0x00000400, 0x08000400, 0x00000408, 0x08000408, - 0x00020000, 0x08020000, 0x00020008, 0x08020008, - 0x00020400, 0x08020400, 0x00020408, 0x08020408, - 0x00000001, 0x08000001, 0x00000009, 0x08000009, - 0x00000401, 0x08000401, 0x00000409, 0x08000409, - 0x00020001, 0x08020001, 0x00020009, 0x08020009, - 0x00020401, 0x08020401, 0x00020409, 0x08020409, - 0x02000000, 0x0A000000, 0x02000008, 0x0A000008, - 0x02000400, 0x0A000400, 0x02000408, 0x0A000408, - 0x02020000, 0x0A020000, 0x02020008, 0x0A020008, - 0x02020400, 0x0A020400, 0x02020408, 0x0A020408, - 0x02000001, 0x0A000001, 0x02000009, 0x0A000009, - 0x02000401, 0x0A000401, 0x02000409, 0x0A000409, - 0x02020001, 0x0A020001, 0x02020009, 0x0A020009, - 0x02020401, 0x0A020401, 0x02020409, 0x0A020409, - }, - { - /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */ - 0x00000000, 0x00000100, 0x00080000, 0x00080100, - 0x01000000, 0x01000100, 0x01080000, 0x01080100, - 0x00000010, 0x00000110, 0x00080010, 0x00080110, - 0x01000010, 0x01000110, 0x01080010, 0x01080110, - 0x00200000, 0x00200100, 0x00280000, 0x00280100, - 0x01200000, 0x01200100, 0x01280000, 0x01280100, - 0x00200010, 0x00200110, 0x00280010, 0x00280110, - 0x01200010, 0x01200110, 0x01280010, 0x01280110, - 0x00000200, 0x00000300, 0x00080200, 0x00080300, - 0x01000200, 0x01000300, 0x01080200, 0x01080300, - 0x00000210, 0x00000310, 0x00080210, 0x00080310, - 0x01000210, 0x01000310, 0x01080210, 0x01080310, - 0x00200200, 0x00200300, 0x00280200, 0x00280300, - 0x01200200, 0x01200300, 0x01280200, 0x01280300, - 0x00200210, 0x00200310, 0x00280210, 0x00280310, - 0x01200210, 0x01200310, 0x01280210, 0x01280310, - }, - { - /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */ - 0x00000000, 0x04000000, 0x00040000, 0x04040000, - 0x00000002, 0x04000002, 0x00040002, 0x04040002, - 0x00002000, 0x04002000, 0x00042000, 0x04042000, - 0x00002002, 0x04002002, 0x00042002, 0x04042002, - 0x00000020, 0x04000020, 0x00040020, 0x04040020, - 0x00000022, 0x04000022, 0x00040022, 0x04040022, - 0x00002020, 0x04002020, 0x00042020, 0x04042020, - 0x00002022, 0x04002022, 0x00042022, 0x04042022, - 0x00000800, 0x04000800, 0x00040800, 0x04040800, - 0x00000802, 0x04000802, 0x00040802, 0x04040802, - 0x00002800, 0x04002800, 0x00042800, 0x04042800, - 0x00002802, 0x04002802, 0x00042802, 0x04042802, - 0x00000820, 0x04000820, 0x00040820, 0x04040820, - 0x00000822, 0x04000822, 0x00040822, 0x04040822, - 0x00002820, 0x04002820, 0x00042820, 0x04042820, - 0x00002822, 0x04002822, 0x00042822, 0x04042822, - }, - }; - - private static final int SPtrans[][] = - { - { - /* nibble 0 */ - 0x00820200, 0x00020000, 0x80800000, 0x80820200, - 0x00800000, 0x80020200, 0x80020000, 0x80800000, - 0x80020200, 0x00820200, 0x00820000, 0x80000200, - 0x80800200, 0x00800000, 0x00000000, 0x80020000, - 0x00020000, 0x80000000, 0x00800200, 0x00020200, - 0x80820200, 0x00820000, 0x80000200, 0x00800200, - 0x80000000, 0x00000200, 0x00020200, 0x80820000, - 0x00000200, 0x80800200, 0x80820000, 0x00000000, - 0x00000000, 0x80820200, 0x00800200, 0x80020000, - 0x00820200, 0x00020000, 0x80000200, 0x00800200, - 0x80820000, 0x00000200, 0x00020200, 0x80800000, - 0x80020200, 0x80000000, 0x80800000, 0x00820000, - 0x80820200, 0x00020200, 0x00820000, 0x80800200, - 0x00800000, 0x80000200, 0x80020000, 0x00000000, - 0x00020000, 0x00800000, 0x80800200, 0x00820200, - 0x80000000, 0x80820000, 0x00000200, 0x80020200, - }, - { - /* nibble 1 */ - 0x10042004, 0x00000000, 0x00042000, 0x10040000, - 0x10000004, 0x00002004, 0x10002000, 0x00042000, - 0x00002000, 0x10040004, 0x00000004, 0x10002000, - 0x00040004, 0x10042000, 0x10040000, 0x00000004, - 0x00040000, 0x10002004, 0x10040004, 0x00002000, - 0x00042004, 0x10000000, 0x00000000, 0x00040004, - 0x10002004, 0x00042004, 0x10042000, 0x10000004, - 0x10000000, 0x00040000, 0x00002004, 0x10042004, - 0x00040004, 0x10042000, 0x10002000, 0x00042004, - 0x10042004, 0x00040004, 0x10000004, 0x00000000, - 0x10000000, 0x00002004, 0x00040000, 0x10040004, - 0x00002000, 0x10000000, 0x00042004, 0x10002004, - 0x10042000, 0x00002000, 0x00000000, 0x10000004, - 0x00000004, 0x10042004, 0x00042000, 0x10040000, - 0x10040004, 0x00040000, 0x00002004, 0x10002000, - 0x10002004, 0x00000004, 0x10040000, 0x00042000, - }, - { - /* nibble 2 */ - 0x41000000, 0x01010040, 0x00000040, 0x41000040, - 0x40010000, 0x01000000, 0x41000040, 0x00010040, - 0x01000040, 0x00010000, 0x01010000, 0x40000000, - 0x41010040, 0x40000040, 0x40000000, 0x41010000, - 0x00000000, 0x40010000, 0x01010040, 0x00000040, - 0x40000040, 0x41010040, 0x00010000, 0x41000000, - 0x41010000, 0x01000040, 0x40010040, 0x01010000, - 0x00010040, 0x00000000, 0x01000000, 0x40010040, - 0x01010040, 0x00000040, 0x40000000, 0x00010000, - 0x40000040, 0x40010000, 0x01010000, 0x41000040, - 0x00000000, 0x01010040, 0x00010040, 0x41010000, - 0x40010000, 0x01000000, 0x41010040, 0x40000000, - 0x40010040, 0x41000000, 0x01000000, 0x41010040, - 0x00010000, 0x01000040, 0x41000040, 0x00010040, - 0x01000040, 0x00000000, 0x41010000, 0x40000040, - 0x41000000, 0x40010040, 0x00000040, 0x01010000, - }, - { - /* nibble 3 */ - 0x00100402, 0x04000400, 0x00000002, 0x04100402, - 0x00000000, 0x04100000, 0x04000402, 0x00100002, - 0x04100400, 0x04000002, 0x04000000, 0x00000402, - 0x04000002, 0x00100402, 0x00100000, 0x04000000, - 0x04100002, 0x00100400, 0x00000400, 0x00000002, - 0x00100400, 0x04000402, 0x04100000, 0x00000400, - 0x00000402, 0x00000000, 0x00100002, 0x04100400, - 0x04000400, 0x04100002, 0x04100402, 0x00100000, - 0x04100002, 0x00000402, 0x00100000, 0x04000002, - 0x00100400, 0x04000400, 0x00000002, 0x04100000, - 0x04000402, 0x00000000, 0x00000400, 0x00100002, - 0x00000000, 0x04100002, 0x04100400, 0x00000400, - 0x04000000, 0x04100402, 0x00100402, 0x00100000, - 0x04100402, 0x00000002, 0x04000400, 0x00100402, - 0x00100002, 0x00100400, 0x04100000, 0x04000402, - 0x00000402, 0x04000000, 0x04000002, 0x04100400, - }, - { - /* nibble 4 */ - 0x02000000, 0x00004000, 0x00000100, 0x02004108, - 0x02004008, 0x02000100, 0x00004108, 0x02004000, - 0x00004000, 0x00000008, 0x02000008, 0x00004100, - 0x02000108, 0x02004008, 0x02004100, 0x00000000, - 0x00004100, 0x02000000, 0x00004008, 0x00000108, - 0x02000100, 0x00004108, 0x00000000, 0x02000008, - 0x00000008, 0x02000108, 0x02004108, 0x00004008, - 0x02004000, 0x00000100, 0x00000108, 0x02004100, - 0x02004100, 0x02000108, 0x00004008, 0x02004000, - 0x00004000, 0x00000008, 0x02000008, 0x02000100, - 0x02000000, 0x00004100, 0x02004108, 0x00000000, - 0x00004108, 0x02000000, 0x00000100, 0x00004008, - 0x02000108, 0x00000100, 0x00000000, 0x02004108, - 0x02004008, 0x02004100, 0x00000108, 0x00004000, - 0x00004100, 0x02004008, 0x02000100, 0x00000108, - 0x00000008, 0x00004108, 0x02004000, 0x02000008, - }, - { - /* nibble 5 */ - 0x20000010, 0x00080010, 0x00000000, 0x20080800, - 0x00080010, 0x00000800, 0x20000810, 0x00080000, - 0x00000810, 0x20080810, 0x00080800, 0x20000000, - 0x20000800, 0x20000010, 0x20080000, 0x00080810, - 0x00080000, 0x20000810, 0x20080010, 0x00000000, - 0x00000800, 0x00000010, 0x20080800, 0x20080010, - 0x20080810, 0x20080000, 0x20000000, 0x00000810, - 0x00000010, 0x00080800, 0x00080810, 0x20000800, - 0x00000810, 0x20000000, 0x20000800, 0x00080810, - 0x20080800, 0x00080010, 0x00000000, 0x20000800, - 0x20000000, 0x00000800, 0x20080010, 0x00080000, - 0x00080010, 0x20080810, 0x00080800, 0x00000010, - 0x20080810, 0x00080800, 0x00080000, 0x20000810, - 0x20000010, 0x20080000, 0x00080810, 0x00000000, - 0x00000800, 0x20000010, 0x20000810, 0x20080800, - 0x20080000, 0x00000810, 0x00000010, 0x20080010, - }, - { - /* nibble 6 */ - 0x00001000, 0x00000080, 0x00400080, 0x00400001, - 0x00401081, 0x00001001, 0x00001080, 0x00000000, - 0x00400000, 0x00400081, 0x00000081, 0x00401000, - 0x00000001, 0x00401080, 0x00401000, 0x00000081, - 0x00400081, 0x00001000, 0x00001001, 0x00401081, - 0x00000000, 0x00400080, 0x00400001, 0x00001080, - 0x00401001, 0x00001081, 0x00401080, 0x00000001, - 0x00001081, 0x00401001, 0x00000080, 0x00400000, - 0x00001081, 0x00401000, 0x00401001, 0x00000081, - 0x00001000, 0x00000080, 0x00400000, 0x00401001, - 0x00400081, 0x00001081, 0x00001080, 0x00000000, - 0x00000080, 0x00400001, 0x00000001, 0x00400080, - 0x00000000, 0x00400081, 0x00400080, 0x00001080, - 0x00000081, 0x00001000, 0x00401081, 0x00400000, - 0x00401080, 0x00000001, 0x00001001, 0x00401081, - 0x00400001, 0x00401080, 0x00401000, 0x00001001, - }, - { - /* nibble 7 */ - 0x08200020, 0x08208000, 0x00008020, 0x00000000, - 0x08008000, 0x00200020, 0x08200000, 0x08208020, - 0x00000020, 0x08000000, 0x00208000, 0x00008020, - 0x00208020, 0x08008020, 0x08000020, 0x08200000, - 0x00008000, 0x00208020, 0x00200020, 0x08008000, - 0x08208020, 0x08000020, 0x00000000, 0x00208000, - 0x08000000, 0x00200000, 0x08008020, 0x08200020, - 0x00200000, 0x00008000, 0x08208000, 0x00000020, - 0x00200000, 0x00008000, 0x08000020, 0x08208020, - 0x00008020, 0x08000000, 0x00000000, 0x00208000, - 0x08200020, 0x08008020, 0x08008000, 0x00200020, - 0x08208000, 0x00000020, 0x00200020, 0x08008000, - 0x08208020, 0x00200000, 0x08200000, 0x08000020, - 0x00208000, 0x00008020, 0x08008020, 0x08200000, - 0x00000020, 0x08208000, 0x00208020, 0x00000000, - 0x08000000, 0x08200020, 0x00008000, 0x00208020 - } - }; - - private static final int cov_2char[] = - { - 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, - 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, - 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A - }; - - private static final int byteToUnsigned(byte b) - { - int value = (int)b; - - return(value >= 0 ? value : value + 256); - } - - private static int fourBytesToInt(byte b[], int offset) - { - int value; - - value = byteToUnsigned(b[offset++]); - value |= (byteToUnsigned(b[offset++]) << 8); - value |= (byteToUnsigned(b[offset++]) << 16); - value |= (byteToUnsigned(b[offset++]) << 24); - - return(value); - } - - private static final void intToFourBytes(int iValue, byte b[], int offset) - { - b[offset++] = (byte)((iValue) & 0xff); - b[offset++] = (byte)((iValue >>> 8 ) & 0xff); - b[offset++] = (byte)((iValue >>> 16) & 0xff); - b[offset++] = (byte)((iValue >>> 24) & 0xff); - } - - private static final void PERM_OP(int a, int b, int n, int m, int results[]) - { - int t; - - t = ((a >>> n) ^ b) & m; - a ^= t << n; - b ^= t; - - results[0] = a; - results[1] = b; - } - - private static final int HPERM_OP(int a, int n, int m) - { - int t; - - t = ((a << (16 - n)) ^ a) & m; - a = a ^ t ^ (t >>> (16 - n)); - - return(a); - } - - private static int [] des_set_key(byte key[]) - { - int schedule[] = new int[ITERATIONS * 2]; - - int c = fourBytesToInt(key, 0); - int d = fourBytesToInt(key, 4); - - int results[] = new int[2]; - - PERM_OP(d, c, 4, 0x0f0f0f0f, results); - d = results[0]; c = results[1]; - - c = HPERM_OP(c, -2, 0xcccc0000); - d = HPERM_OP(d, -2, 0xcccc0000); - - PERM_OP(d, c, 1, 0x55555555, results); - d = results[0]; c = results[1]; - - PERM_OP(c, d, 8, 0x00ff00ff, results); - c = results[0]; d = results[1]; - - PERM_OP(d, c, 1, 0x55555555, results); - d = results[0]; c = results[1]; - - d = (((d & 0x000000ff) << 16) | (d & 0x0000ff00) | - ((d & 0x00ff0000) >>> 16) | ((c & 0xf0000000) >>> 4)); - c &= 0x0fffffff; - - int s, t; - int j = 0; - - for(int i = 0; i < ITERATIONS; i ++) - { - if(shifts2[i]) - { - c = (c >>> 2) | (c << 26); - d = (d >>> 2) | (d << 26); - } - else - { - c = (c >>> 1) | (c << 27); - d = (d >>> 1) | (d << 27); - } - - c &= 0x0fffffff; - d &= 0x0fffffff; - - s = skb[0][ (c ) & 0x3f ]| - skb[1][((c >>> 6) & 0x03) | ((c >>> 7) & 0x3c)]| - skb[2][((c >>> 13) & 0x0f) | ((c >>> 14) & 0x30)]| - skb[3][((c >>> 20) & 0x01) | ((c >>> 21) & 0x06) | - ((c >>> 22) & 0x38)]; - - t = skb[4][ (d ) & 0x3f ]| - skb[5][((d >>> 7) & 0x03) | ((d >>> 8) & 0x3c)]| - skb[6][ (d >>>15) & 0x3f ]| - skb[7][((d >>>21) & 0x0f) | ((d >>> 22) & 0x30)]; - - schedule[j++] = ((t << 16) | (s & 0x0000ffff)) & 0xffffffff; - s = ((s >>> 16) | (t & 0xffff0000)); - - s = (s << 4) | (s >>> 28); - schedule[j++] = s & 0xffffffff; - } - return(schedule); - } - - private static final int D_ENCRYPT - ( - int L, int R, int S, int E0, int E1, int s[] - ) - { - int t, u, v; - - v = R ^ (R >>> 16); - u = v & E0; - v = v & E1; - u = (u ^ (u << 16)) ^ R ^ s[S]; - t = (v ^ (v << 16)) ^ R ^ s[S + 1]; - t = (t >>> 4) | (t << 28); - - L ^= SPtrans[1][(t ) & 0x3f] | - SPtrans[3][(t >>> 8) & 0x3f] | - SPtrans[5][(t >>> 16) & 0x3f] | - SPtrans[7][(t >>> 24) & 0x3f] | - SPtrans[0][(u ) & 0x3f] | - SPtrans[2][(u >>> 8) & 0x3f] | - SPtrans[4][(u >>> 16) & 0x3f] | - SPtrans[6][(u >>> 24) & 0x3f]; - - return(L); - } - - private static final int [] body(int schedule[], int Eswap0, int Eswap1) - { - int left = 0; - int right = 0; - int t = 0; - - for(int j = 0; j < 25; j ++) - { - for(int i = 0; i < ITERATIONS * 2; i += 4) - { - left = D_ENCRYPT(left, right, i, Eswap0, Eswap1, schedule); - right = D_ENCRYPT(right, left, i + 2, Eswap0, Eswap1, schedule); - } - t = left; - left = right; - right = t; - } - - t = right; - - right = (left >>> 1) | (left << 31); - left = (t >>> 1) | (t << 31); - - left &= 0xffffffff; - right &= 0xffffffff; - - int results[] = new int[2]; - - PERM_OP(right, left, 1, 0x55555555, results); - right = results[0]; left = results[1]; - - PERM_OP(left, right, 8, 0x00ff00ff, results); - left = results[0]; right = results[1]; - - PERM_OP(right, left, 2, 0x33333333, results); - right = results[0]; left = results[1]; - - PERM_OP(left, right, 16, 0x0000ffff, results); - left = results[0]; right = results[1]; - - PERM_OP(right, left, 4, 0x0f0f0f0f, results); - right = results[0]; left = results[1]; - - int out[] = new int[2]; - - out[0] = left; out[1] = right; - - return(out); - } - - public static final String crypt(String salt, String original) - { - while(salt.length() < 2) - salt += "A"; - - StringBuffer buffer = new StringBuffer(" "); - - char charZero = salt.charAt(0); - char charOne = salt.charAt(1); - - buffer.setCharAt(0, charZero); - buffer.setCharAt(1, charOne); - - int Eswap0 = con_salt[(int)charZero]; - int Eswap1 = con_salt[(int)charOne] << 4; - - byte key[] = new byte[8]; - - for(int i = 0; i < key.length; i ++) - key[i] = (byte)0; - - for(int i = 0; i < key.length && i < original.length(); i ++) - { - int iChar = (int)original.charAt(i); - - key[i] = (byte)(iChar << 1); - } - - int schedule[] = des_set_key(key); - int out[] = body(schedule, Eswap0, Eswap1); - - byte b[] = new byte[9]; - - intToFourBytes(out[0], b, 0); - intToFourBytes(out[1], b, 4); - b[8] = 0; - - for(int i = 2, y = 0, u = 0x80; i < 13; i ++) - { - for(int j = 0, c = 0; j < 6; j ++) - { - c <<= 1; - - if(((int)b[y] & u) != 0) - c |= 1; - - u >>>= 1; - - if(u == 0) - { - y++; - u = 0x80; - } - buffer.setCharAt(i, (char)cov_2char[c]); - } - } - return(buffer.toString()); - } - - public static void main(String args[]) - { - if(args.length >= 2) - { - System.out.println - ( - "[" + args[0] + "] [" + args[1] + "] => [" + - Crypt.crypt(args[0], args[1]) + "]" - ); - } - } -} diff --git a/src/helma/util/CryptFile.java b/src/helma/util/CryptFile.java deleted file mode 100644 index e33228c0..00000000 --- a/src/helma/util/CryptFile.java +++ /dev/null @@ -1,78 +0,0 @@ -// CryptFile.java -// Copyright (c) Hannes Wallnöfer 2001 - -package helma.util; - -import java.util.*; -import java.io.*; - -/** - * 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 (); - } - - - 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; - } - - 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")); - } - -} diff --git a/src/helma/util/HtmlEncoder.java b/src/helma/util/HtmlEncoder.java index 63d6f027..b54d7419 100644 --- a/src/helma/util/HtmlEncoder.java +++ b/src/helma/util/HtmlEncoder.java @@ -18,7 +18,6 @@ import java.text.*; public final class HtmlEncoder { - /* static final Hashtable convertor = new Hashtable (128); // conversion table @@ -119,70 +118,70 @@ public final class HtmlEncoder { convertor.put(new Integer(253), "ý"); convertor.put(new Integer(254), "þ"); convertor.put(new Integer(255), "ÿ"); - } */ + } /** * */ - public final static String encode (String str) { + public final static String encode (String what) { // try to make stringbuffer large enough from the start - StringBuffer ret = new StringBuffer (Math.round (str.length()*1.4f)); - encode (str, ret); + StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f)); + encode (what, ret); return ret.toString(); } /** * */ - public final static void encode (String str, StringBuffer ret) { - if (str == null) + public final static void encode (String what, StringBuffer ret) { + if (what == null || what.length() == 0) { return; - - int l = str.length(); - + } + + StringReader in = new StringReader (what); + int c; boolean closeTag=false, readTag=false, tagOpen=false; // the difference between swallowOneNewline and ignoreNewline is that swallowOneNewline is just effective once (for the next newline) - boolean ignoreNewline = false; + boolean ignoreNewline = false; boolean swallowOneNewline = false; StringBuffer tag = new StringBuffer (); - - for (int i=0; i"); + ret.append ('\n'); if (!tagOpen) swallowOneNewline = false; break; @@ -197,28 +196,28 @@ public final class HtmlEncoder { ret.append ('>'); break; default: - ret.append (c); - // if (c < 160) - // ret.append ((char) c); - // else if (c >= 160 && c <= 255) - // ret.append (convertor.get(new Integer(c))); - // else { - // ret.append ("&#"); - // ret.append (c); - // ret.append (";"); - // } - if (!tagOpen && !Character.isWhitespace (c)) + if (c < 160) + ret.append ((char) c); + else if (c >= 160 && c <= 255) + ret.append (convertor.get(new Integer(c))); + else { + ret.append ("&#"); + ret.append (c); + ret.append (";"); + } + if (!tagOpen && !Character.isWhitespace ((char)c)) swallowOneNewline = false; + } } - } + } catch (IOException e) {} } /** * */ - public final static String encodeFormValue (String str) { - StringBuffer ret = new StringBuffer (Math.round (str.length()*1.2f)); - encodeAll (str, ret, false); + public final static String encodeFormValue (String what) { + StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f)); + encodeAll (what, ret, false); return ret.toString(); } @@ -226,17 +225,17 @@ public final class HtmlEncoder { /** * */ - public final static String encodeAll (String str) { - StringBuffer ret = new StringBuffer (Math.round (str.length()*1.2f)); - encodeAll (str, ret, true); + public final static String encodeAll (String what) { + StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f)); + encodeAll (what, ret, true); return ret.toString(); } /** * */ - public final static String encodeAll (String str, StringBuffer ret) { - encodeAll (str, ret, true); + public final static String encodeAll (String what, StringBuffer ret) { + encodeAll (what, ret, true); return ret.toString(); } @@ -244,76 +243,115 @@ public final class HtmlEncoder { /** * */ - public final static void encodeAll (String str, StringBuffer ret, boolean encodeNewline) { - if (str == null) + public final static void encodeAll (String what, StringBuffer ret, boolean encodeNewline) { + if (what == null || what.length() == 0) { return; + } - int l = str.length(); - for (int i=0; i': + case '>': ret.append (">"); break; - case '&': + case '&': ret.append ("&"); break; - case '"': + case '"': ret.append ("""); break; - case '\n': - ret.append ('\n'); + case '\n': if (encodeNewline) { ret.append ("
"); + break; } - break; - default: - ret.append (c); - // if (c < 160) - // ret.append ((char) c); - // else if (c >= 160 && c <= 255) - // ret.append (convertor.get(new Integer(c))); - // else { - // ret.append ("&#"); - // ret.append (c); - // ret.append (";"); - // } + default: + if (c < 160) + ret.append ((char) c); + else if (c >= 160 && c <= 255) + ret.append (convertor.get(new Integer(c))); + else { + ret.append ("&#"); + ret.append (c); + ret.append (";"); + } + } } + } catch (IOException e) {} + } + + public final static String encodeSoft (String what) { + StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f)); + encodeSoft (what, ret); + return ret.toString(); + } + + public final static void encodeSoft (String what, StringBuffer ret) { + if (what == null || what.length() == 0) { + return; } + + StringReader in = new StringReader (what); + int c; + try { + while ((c = in.read()) != -1) { + switch (c) { + case 128: // Euro-Symbol. This is for missing Unicode support in TowerJ. + ret.append ("€"); + break; + default: + if (c < 160) + ret.append ((char) c); + else if (c >= 160 && c <= 255) + ret.append (convertor.get(new Integer(c))); + else { + ret.append ("&#"); + ret.append (c); + ret.append (";"); + } + } + } + } catch (IOException e) {} } - public final static String encodeXml (String str) { - StringBuffer ret = new StringBuffer (Math.round (str.length()*1.2f)); - encodeXml (str, ret); + public final static String encodeXml (String what) { + StringBuffer ret = new StringBuffer (Math.round (what.length()*1.4f)); + encodeXml (what, ret); return ret.toString(); } - public final static void encodeXml (String str, StringBuffer ret) { - if (str == null) + public final static void encodeXml (String what, StringBuffer ret) { + if (what == null || what.length() == 0) { return; + } - int l = str.length(); - for (int i=0; i': + case '>': ret.append (">"); break; - case '&': + case '&': ret.append ("&"); break; - default: - ret.append (c); + default: + ret.append ((char) c); + } } - } + } catch (IOException e) {} } -} // end of class + +} diff --git a/src/helma/util/Logger.java b/src/helma/util/Logger.java index 933cd931..6a5e3ac6 100644 --- a/src/helma/util/Logger.java +++ b/src/helma/util/Logger.java @@ -6,353 +6,105 @@ package helma.util; import java.io.*; import java.util.*; import java.text.*; -import java.util.zip.GZIPOutputStream; /** * Utility class for asynchronous logging. */ -public final class Logger { +public class Logger implements Runnable { - // we use one static thread for all Loggers - static Runner runner; - // the list of active loggers - static ArrayList loggers; - // hash map of loggers - static HashMap loggerMap; + private Thread logger; - // buffer for log items - private List entries; - - // fields used for logging to files + private Vector entries; private String filename; - private File logdir; - private File logfile; - private PrintWriter writer; - // the canonical name for this logger - String canonicalName; - - // used when logging to a PrintStream such as System.out + private String dirname; + private File dir; + private File currentFile; + private PrintWriter currentWriter; + private int fileindex = 0; + private DecimalFormat nformat; + private DateFormat dformat; + private long dateLastRendered; + private String dateCache; private PrintStream out = null; - // flag to tell runner thread if this log should be closed/discarded - boolean closed = false; - - // fields for date rendering and caching - static DateFormat dformat = new SimpleDateFormat ("[yyyy/MM/dd HH:mm] "); - static long dateLastRendered; - static String dateCache; - // number format for log file rotation - DecimalFormat nformat = new DecimalFormat ("000"); - DateFormat aformat = new SimpleDateFormat ("yyyy-MM-dd"); - - - - /** - * Create a logger for a PrintStream, such as System.out. - */ public Logger (PrintStream out) { + dformat = DateFormat.getInstance (); this.out = out; - canonicalName = out.toString (); - - // create a synchronized list for log entries since different threads may - // attempt to modify the list at the same time - entries = Collections.synchronizedList (new LinkedList ()); - - // register this instance with static logger list - start (this); + entries = new Vector (); + logger = new Thread (this); + // logger.setPriority (Thread.MIN_PRIORITY+2); + logger.start (); } - /** - * Create a file logger. The actual file names do have numbers appended and are - * rotated every x bytes. - */ - private Logger (String dirname, String filename) { - this.filename = filename; - logdir = new File (dirname); - logfile = new File (logdir, filename+".log"); - - try { - canonicalName = logfile.getCanonicalPath (); - } catch (IOException iox) { - canonicalName = logfile.getAbsolutePath (); - } - - if (!logdir.exists()) - logdir.mkdirs (); - - try { - rotateLogFile (); - } catch (IOException iox) { - System.err.println ("Error creating log "+canonicalName+": "+iox); - } - - // create a synchronized list for log entries since different threads may - // attempt to modify the list at the same time - entries = Collections.synchronizedList (new LinkedList ()); - - // register this instance with static logger list - start (this); - } - - - /** - * Get a logger with a symbolic file name within a directory. - */ - public static synchronized Logger getLogger (String dirname, String filename) { + public Logger (String dirname, String filename) throws IOException { if (filename == null || dirname == null) - throw new RuntimeException ("Logger can't use null as file or directory name"); - File file = new File (dirname, filename+".log"); - Logger log = null; - if (loggerMap != null) try { - log = (Logger) loggerMap.get (file.getCanonicalPath()); - } catch (IOException iox) { - log = (Logger) loggerMap.get (file.getAbsolutePath()); - } - if (log == null || log.isClosed ()) - log = new Logger (dirname, filename); - return log; + throw new IOException ("Logger can't use null as file or directory name"); + this.filename = filename; + this.dirname = dirname; + nformat = new DecimalFormat ("00000"); + dformat = DateFormat.getInstance (); + dir = new File (dirname); + if (!dir.exists()) + dir.mkdirs (); + currentFile = new File (dir, filename+nformat.format(++fileindex)+".log"); + while (currentFile.exists()) + currentFile = new File (dir, filename+nformat.format(++fileindex)+".log"); + currentWriter = new PrintWriter (new FileWriter (currentFile), false); + entries = new Vector (); + logger = new Thread (this); + // logger.setPriority (Thread.MIN_PRIORITY+2); + logger.start (); } - - /** - * Append a message to the log. - */ 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 15 seconds if (System.currentTimeMillis () - 15000 > dateLastRendered) renderDate (); - entries.add (dateCache + msg); + entries.addElement (dateCache + " " + msg); } - private static synchronized void renderDate () { + private synchronized void renderDate () { dateLastRendered = System.currentTimeMillis (); dateCache = dformat.format (new Date()); } - - /** - * Return an object which identifies this logger. - */ - public String getCanonicalName () { - return canonicalName; - } - - - /** - * Get the list of unwritten 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 { - if (logfile != null && - (logfile.length() > 10000000 || writer == null || - !logfile.exists() || !logfile.canWrite())) { - // rotate log files each 10 megs of if we can't write to it - rotateLogFile (); - } - - int l = entries.size(); - - // check if writing to printstream or file - if (out != null) { + public void run () { + while (Thread.currentThread () == logger) { + try { + if (currentFile != null && currentFile.length() > 10000000) { + // rotate log files each 10 megs + swapFile (); + } + + int l = entries.size(); 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 (); + if (currentWriter != null) + currentWriter.flush (); + logger.sleep (1000l); + + } catch (InterruptedException ir) { + Thread.currentThread().interrupt (); } } } - /** - * Rotate log files, closing, renaming and gzipping the old file and - * start a new one. - */ - private void rotateLogFile () throws IOException { - if (writer != null) try { - writer.close(); - } catch (Exception ignore) {} - if (logfile.exists()) { - String today = aformat.format(new Date()); - int ct=0; - File archive = null; - while (archive==null || archive.exists()) { - String archidx = ct>999 ? Integer.toString(ct) : nformat.format (++ct); - String archname = filename+"-"+today+"-"+ archidx +".log.gz"; - archive = new File (logdir, archname); - } - if (logfile.renameTo (archive)) - (new GZipper(archive)).start(); - else - System.err.println ("Error rotating log file "+canonicalName+". Old file will possibly be overwritten!"); + private void swapFile () { + try { + currentWriter.close(); + currentFile = new File (dir, filename+nformat.format(++fileindex)+".log"); + currentWriter = new PrintWriter (new FileWriter (currentFile), false); + } catch (IOException iox) { + System.err.println ("Error swapping Log files: "+iox); } - writer = new PrintWriter (new FileWriter (logfile), false); - } - - /** - * Tell whether this log is 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; - } - - /** - * Actually closes the file writer of a log. - */ - void closeFiles () { - if (writer != null) try { - writer.close (); - } catch (Exception ignore) {} - } - - /** - * Return a string representation of this Logger - */ - 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 (); - - 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 (); - } - - /** - * The static runner class that loops through all loggers. - */ - static class Runner extends Thread { - - public void run () { - while (runner == this && !isInterrupted ()) { - 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); - } - } - try { - sleep (500); - } catch (InterruptedException ix) {} - } - } - - } - - /** - * 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"); - } - - 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()); - } - } - } - - - - - /** - * 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<50000; i++) - log.log ("test log entry "+i); - 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 deleted file mode 100644 index 6464b8ea..00000000 --- a/src/helma/util/Logo.java +++ /dev/null @@ -1,91 +0,0 @@ -package helma.util; - -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 - }; - - /** - * 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"); - - } - - -} diff --git a/src/helma/util/MD5Encoder.java b/src/helma/util/MD5Encoder.java deleted file mode 100644 index 32b296fd..00000000 --- a/src/helma/util/MD5Encoder.java +++ /dev/null @@ -1,37 +0,0 @@ -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"); - System.exit(0); - } - System.out.println( "adminUsername=" + encode(args[0]) ); - System.out.println( "adminPassword=" + encode(args[1]) ); - } - - public static String encode(String str) throws NoSuchAlgorithmException { - md = MessageDigest.getInstance("MD5"); - byte[] b = md.digest(str.getBytes()); - StringBuffer buf = new StringBuffer(); - for ( int i=0; i -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; - } - } - - public String getName () { - return name; - } - - -} diff --git a/src/helma/util/MimePartDataSource.java b/src/helma/util/MimePartDataSource.java deleted file mode 100644 index a120afe0..00000000 --- a/src/helma/util/MimePartDataSource.java +++ /dev/null @@ -1,45 +0,0 @@ -// MimePartDataSource.java -// Copyright (c) Hannes Wallnöfer 1999-2000 - -package helma.util; - -import javax.activation.*; -import java.io.*; - -/** - * 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) { - this.part = part; - this.name = part.getName (); - } - - public MimePartDataSource (MimePart part, String name) { - this.part = part; - this.name = name; - } - - - public InputStream getInputStream() throws IOException { - return new ByteArrayInputStream(part.getContent ()); - } - - public OutputStream getOutputStream () throws IOException { - throw new IOException ("Can't write to MimePart object."); - } - - public String getContentType() { - return part.contentType; - } - - public String getName () { - return name; - } - -} diff --git a/src/helma/util/Updatable.java b/src/helma/util/Updatable.java deleted file mode 100644 index fb51632a..00000000 --- a/src/helma/util/Updatable.java +++ /dev/null @@ -1,50 +0,0 @@ -// Updatable.java -// Copyright (c) Hannes Wallnöfer 2001 - -package helma.util; - - -/** - * An interface of classes that can update themselves and know when to do so. - */ - - -public interface Updatable { - - public boolean needsUpdate (); - - public void update (); - -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/helma/util/Uploader.java b/src/helma/util/Uploader.java index 6618d424..360d3211 100644 --- a/src/helma/util/Uploader.java +++ b/src/helma/util/Uploader.java @@ -3,7 +3,8 @@ package helma.util; -import helma.util.mime.*; +import helma.mime.*; +import helma.objectmodel.*; import java.io.*; import java.util.*; @@ -75,8 +76,9 @@ public class Uploader { filename = filename.substring (sep+1); } if (filename != null) { - MimePart part = new MimePart (filename, newb, type); - parts.put (name, part); + Node node = new Node (filename); + node.setContent (newb, type); + parts.put (name, node); } else { parts.put (name, new String (newb)); } diff --git a/src/helma/util/UrlEncoder.java b/src/helma/util/UrlEncoder.java new file mode 100644 index 00000000..927f7671 --- /dev/null +++ b/src/helma/util/UrlEncoder.java @@ -0,0 +1,126 @@ +/* + * @(#)URLEncoder.java 1.12 98/07/01 + * + * Copyright 1995-1998 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. + */ + +// Repackaged by Hannes Wallnoefer because this is the only way to +// encode space as %20 (no, subclassing doesn't work here). + +package helma.util; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import java.io.IOException; +import java.util.BitSet; + +/** + * The class contains a utility method for converting a + * String into a MIME format called + * "x-www-form-urlencoded" format. + *

+ * To convert a String, each character is examined in turn: + *

    + *
  • The ASCII characters 'a' through 'z', + * 'A' through 'Z', and '0' + * through '9' remain the same. + *
  • All other characters are converted into the 3-character string + * "%xy", where xy is the two-digit + * hexadecimal representation of the lower 8-bits of the character. + *
+ * + * @author Herb Jellinek + * @version 1.12, 07/01/98 + * @since JDK1.0 + */ +public class UrlEncoder { + static BitSet dontNeedEncoding; + static final int caseDiff = ('a' - 'A'); + + /* The list of characters that are not encoded have been determined by + referencing O'Reilly's "HTML: The Definitive Guide" (page 164). */ + + static { + dontNeedEncoding = new BitSet(256); + int i; + for (i = 'a'; i <= 'z'; i++) { + dontNeedEncoding.set(i); + } + for (i = 'A'; i <= 'Z'; i++) { + dontNeedEncoding.set(i); + } + for (i = '0'; i <= '9'; i++) { + dontNeedEncoding.set(i); + } + // dontNeedEncoding.set(' '); /* removed to encode space as %20 */ + dontNeedEncoding.set('-'); + dontNeedEncoding.set('_'); + dontNeedEncoding.set('.'); + dontNeedEncoding.set('*'); + } + + /** + * You can't call the constructor. + */ + // private URLEncoder() { } + + /** + * Translates a string into x-www-form-urlencoded format. + * + * @param s String to be translated. + * @return the translated String. + * @since JDK1.0 + */ + public static String encode(String s) { + int maxBytesPerChar = 10; + ByteArrayOutputStream out = new ByteArrayOutputStream(s.length()); + ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar); + OutputStreamWriter writer = new OutputStreamWriter(buf); + + for (int i = 0; i < s.length(); i++) { + int c = (int)s.charAt(i); + if (dontNeedEncoding.get(c)) { + if (c == ' ') { + c = '+'; + } + out.write(c); + } else { + // convert to external encoding before hex conversion + try { + writer.write(c); + writer.flush(); + } catch(IOException e) { + buf.reset(); + continue; + } + byte[] ba = buf.toByteArray(); + for (int j = 0; j < ba.length; j++) { + out.write('%'); + char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16); + // converting to use uppercase letter as part of + // the hex value if ch is a letter. + if (Character.isLetter(ch)) { + ch -= caseDiff; + } + out.write(ch); + ch = Character.forDigit(ba[j] & 0xF, 16); + if (Character.isLetter(ch)) { + ch -= caseDiff; + } + out.write(ch); + } + buf.reset(); + } + } + + return out.toString(); + } +} diff --git a/src/helma/xmlrpc/Base64.java b/src/helma/xmlrpc/Base64.java index 86cf8e5f..6d6380ed 100644 --- a/src/helma/xmlrpc/Base64.java +++ b/src/helma/xmlrpc/Base64.java @@ -1,286 +1,130 @@ -//////////////////////license & copyright header///////////////////////// -// // -// Base64 - encode/decode data using the Base64 encoding scheme // -// // -// Copyright (c) 1998 by Kevin Kelley // -// // -// This library is free software; you can redistribute it and/or // -// modify it under the terms of the GNU Lesser General Public // -// License as published by the Free Software Foundation; either // -// version 2.1 of the License, or (at your option) any later version. // -// // -// This library is distributed in the hope that it will be useful, // -// but WITHOUT ANY WARRANTY; without even the implied warranty of // -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // -// GNU Lesser General Public License for more details. // -// // -// You should have received a copy of the GNU Lesser General Public // -// License along with this library; if not, write to the Free Software // -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // -// 02111-1307, USA, or contact the author: // -// // -// Kevin Kelley - 30718 Rd. 28, La Junta, CO, // -// 81050 USA. // -// // -////////////////////end license & copyright header/////////////////////// - package helma.xmlrpc; -import java.io.*; // needed only for main() method. - - /** -* Provides encoding of raw bytes to base64-encoded characters, and -* decoding of base64 characters to raw bytes. -* -* @author Kevin Kelley (kelley@ruralnet.net) -* @version 1.3 -* @date 06 August 1998 -* @modified 14 February 2000 -* @modified 22 September 2000 -*/ -public class Base64 { + * Provides encoding of raw bytes to base64-encoded characters, and + * decoding of base64 characters to raw bytes. + * + * @author Kevin Kelley (kelley@iguana.ruralnet.net) + * @version 1.0 + * @date 06 August 1998 + */ -/** -* returns an array of base64-encoded characters to represent the -* passed data array. -* -* @param data the array of bytes to encode -* @return base64-coded character array. -*/ -static public char[] encode(byte[] data) +public class Base64 { - char[] out = new char[((data.length + 2) / 3) * 4]; + /** + * returns an array of base64-encoded characters to represent the + * passed data array. + * + * @param data the array of bytes to encode + * @return base64-coded character array. + */ + + static public char[] encode(byte[] data) { + + char[] out = new char[((data.length + 2) / 3) * 4]; + // + // 3 bytes encode to 4 chars. Output is always an even + // multiple of 4 characters. + // + + for (int i = 0, index = 0; i < data.length; i += 3, + index += 4) { + boolean quad = false; + boolean trip = false; + int val = (0xFF & (int) data[i]); + + val <<= 8; + if ((i + 1) < data.length) { + val |= (0xFF & (int) data[i + 1]); + trip = true; + } + val <<= 8; + if ((i + 2) < data.length) { + val |= (0xFF & (int) data[i + 2]); + quad = true; + } + out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)]; + val >>= 6; + out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)]; + val >>= 6; + out[index + 1] = alphabet[val & 0x3F]; + val >>= 6; + out[index + 0] = alphabet[val & 0x3F]; + } + return out; + } + + + + /** + * Returns an array of bytes which were encoded in the passed + * character array. + * + * @param data the array of base64-encoded characters + * @return decoded data array + */ + static public byte[] decode(byte[] data) { + int len = ((data.length + 3) / 4) * 3; + if (data.length > 0 && data[data.length - 1] == '=') + --len; + if (data.length > 1 && data[data.length - 2] == '=') + --len; + byte[] out = new byte[len]; + + int shift = 0; // # of excess bits stored in accum + int accum = 0; // excess bits + int index = 0; + + for (int ix = 0; ix < data.length; ix++) { + int value = codes[data[ix] & 0xFF]; // ignore high byte of char + if (value >= 0) { + // skip over non-code + accum <<= 6; // bits shift up by 6 each time thru + shift += 6; // loop, with new bits being put in + accum |= value; // at the bottom. + if (shift >= 8) { + // whenever there are 8 or more shifted in, + shift -= 8; // write them out (from the top, leaving any + // excess at the bottom for next iteration. + out[index++] = (byte)((accum >> shift) & 0xff); + } + } + } + + if (index != out.length) throw new RuntimeException("Error decoding BASE64 element: miscalculated data length!"); + + return out; + } // - // 3 bytes encode to 4 chars. Output is always an even - // multiple of 4 characters. + // code characters for values 0..63 // - for (int i=0, index=0; i>= 6; - out[index+2] = alphabet[(trip? (val & 0x3F): 64)]; - val >>= 6; - out[index+1] = alphabet[val & 0x3F]; - val >>= 6; - out[index+0] = alphabet[val & 0x3F]; - } - return out; -} + static private char[] alphabet = + + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" + .toCharArray(); - /** - * Decodes a BASE-64 encoded stream to recover the original - * data. White space before and after will be trimmed away, - * but no other manipulation of the input will be performed. - * - * As of version 1.2 this method will properly handle input - * containing junk characters (newlines and the like) rather - * than throwing an error. It does this by pre-parsing the - * input and generating from that a count of VALID input - * characters. - **/ -static public byte[] decode(char[] data) -{ - // as our input could contain non-BASE64 data (newlines, - // whitespace of any sort, whatever) we must first adjust - // our count of USABLE data so that... - // (a) we don't misallocate the output array, and - // (b) think that we miscalculated our data length - // just because of extraneous throw-away junk + // + // lookup table for converting base64 characters to value in range 0..63 + // - int tempLen = data.length; - for( int ix=0; ix 255) || codes[ data[ix] ] < 0 ) - --tempLen; // ignore non-valid chars and padding - } - // calculate required length: - // -- 3 bytes for every 4 valid base64 chars - // -- plus 2 bytes if there are 3 extra base64 chars, - // or plus 1 byte if there are 2 extra. + static private byte[] codes = new byte[256]; + + static { + for (int i = 0; i < 256; i++) + codes[i] = -1; + + for (int i = 'A'; i <= 'Z'; i++) + codes[i] = (byte)(i - 'A'); + + for (int i = 'a'; i <= 'z'; i++) + codes[i] = (byte)(26 + i - 'a'); - int len = (tempLen / 4) * 3; - if ((tempLen % 4) == 3) len += 2; - if ((tempLen % 4) == 2) len += 1; + for (int i = '0'; i <= '9'; i++) + codes[i] = (byte)(52 + i - '0'); - byte[] out = new byte[len]; - - - - int shift = 0; // # of excess bits stored in accum - int accum = 0; // excess bits - int index = 0; - - // we now go through the entire array (NOT using the 'tempLen' value) - for (int ix=0; ix255)? -1: codes[ data[ix] ]; - - if ( value >= 0 ) // skip over non-code - { - accum <<= 6; // bits shift up by 6 each time thru - shift += 6; // loop, with new bits being put in - accum |= value; // at the bottom. - if ( shift >= 8 ) // whenever there are 8 or more shifted in, - { - shift -= 8; // write them out (from the top, leaving any - out[index++] = // excess at the bottom for next iteration. - (byte) ((accum >> shift) & 0xff); - } - } - // we will also have skipped processing a padding null byte ('=') here; - // these are used ONLY for padding to an even length and do not legally - // occur as encoded data. for this reason we can ignore the fact that - // no index++ operation occurs in that special case: the out[] array is - // initialized to all-zero bytes to start with and that works to our - // advantage in this combination. - } - - // if there is STILL something wrong we just have to throw up now! - if( index != out.length) - { - throw new Error("Miscalculated data length (wrote " + index + " instead of " + out.length + ")"); - } - - return out; -} - - -// -// code characters for values 0..63 -// -static private char[] alphabet = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" - .toCharArray(); - -// -// lookup table for converting base64 characters to value in range 0..63 -// -static private byte[] codes = new byte[256]; -static { - for (int i=0; i<256; i++) codes[i] = -1; - for (int i = 'A'; i <= 'Z'; i++) codes[i] = (byte)( i - 'A'); - for (int i = 'a'; i <= 'z'; i++) codes[i] = (byte)(26 + i - 'a'); - for (int i = '0'; i <= '9'; i++) codes[i] = (byte)(52 + i - '0'); - codes['+'] = 62; - codes['/'] = 63; -} - - - - -/////////////////////////////////////////////////// -// remainder (main method and helper functions) is -// for testing purposes only, feel free to clip it. -/////////////////////////////////////////////////// - -public static void main(String[] args) -{ - boolean decode = false; - - if (args.length == 0) { - System.out.println("usage: java Base64 [-d[ecode]] filename"); - System.exit(0); - } - for (int i=0; i 0) baos.write(buf, 0, count); - } - is.close(); - } - catch (Exception e) { e.printStackTrace(); } - - return baos.toByteArray(); -} - -private static char[] readChars(File file) -{ - CharArrayWriter caw = new CharArrayWriter(); - try - { - Reader fr = new FileReader(file); - Reader in = new BufferedReader(fr); - int count = 0; - char[] buf = new char[16384]; - while ((count=in.read(buf)) != -1) { - if (count > 0) caw.write(buf, 0, count); - } - in.close(); - } - catch (Exception e) { e.printStackTrace(); } - - return caw.toCharArray(); -} - -private static void writeBytes(File file, byte[] data) { - try { - OutputStream fos = new FileOutputStream(file); - OutputStream os = new BufferedOutputStream(fos); - os.write(data); - os.close(); - } - catch (Exception e) { e.printStackTrace(); } -} - -private static void writeChars(File file, char[] data) { - try { - Writer fos = new FileWriter(file); - Writer os = new BufferedWriter(fos); - os.write(data); - os.close(); - } - catch (Exception e) { e.printStackTrace(); } -} -/////////////////////////////////////////////////// -// end of test code. -/////////////////////////////////////////////////// - -} diff --git a/src/helma/xmlrpc/test/Benchmark.java b/src/helma/xmlrpc/Benchmark.java similarity index 98% rename from src/helma/xmlrpc/test/Benchmark.java rename to src/helma/xmlrpc/Benchmark.java index f876c4c4..c64b3833 100644 --- a/src/helma/xmlrpc/test/Benchmark.java +++ b/src/helma/xmlrpc/Benchmark.java @@ -2,9 +2,8 @@ * Copyright 1999 Hannes Wallnoefer */ -package helma.xmlrpc.test; +package helma.xmlrpc; -import helma.xmlrpc.*; import java.util.*; import java.io.IOException; diff --git a/src/helma/xmlrpc/WebServer.java b/src/helma/xmlrpc/WebServer.java index f6bbd219..d99a9595 100644 --- a/src/helma/xmlrpc/WebServer.java +++ b/src/helma/xmlrpc/WebServer.java @@ -219,9 +219,6 @@ public class WebServer implements Runnable { if (listener != null) { Thread l = listener; listener = null; - try { - serverSocket.close (); - } catch (Exception ignore) {} l.interrupt (); } } @@ -399,7 +396,7 @@ class Connection implements Runnable { private void parseAuth (String line) { try { - byte[] c = Base64.decode (line.substring (21).toCharArray ()); + byte[] c = Base64.decode (line.substring (21).getBytes()); String str = new String (c); int col = str.indexOf (":"); user = str.substring (0, col); diff --git a/src/helma/xmlrpc/XmlRpc.java b/src/helma/xmlrpc/XmlRpc.java index 2a745229..d1c72459 100644 --- a/src/helma/xmlrpc/XmlRpc.java +++ b/src/helma/xmlrpc/XmlRpc.java @@ -64,6 +64,7 @@ public abstract class XmlRpc extends HandlerBase { static final int BASE64 = 5; static final int STRUCT = 6; static final int ARRAY = 7; + static final int NIL = 8; // Error level + message int errorLevel; @@ -78,7 +79,7 @@ public abstract class XmlRpc extends HandlerBase { // for debugging output public static boolean debug = false; - final static String types[] = {"String", "Integer", "Boolean", "Double", "Date", "Base64", "Struct", "Array"}; + final static String types[] = {"String", "Integer", "Boolean", "Double", "Date", "Base64", "Struct", "Array", "Nil"}; // mapping between java encoding names and "real" names used in XML prolog. // if you use an encoding not listed here send feedback to xmlrpc@helma.org @@ -175,7 +176,7 @@ public abstract class XmlRpc extends HandlerBase { long now = System.currentTimeMillis (); if (parserClass == null) { // try to get the name of the SAX driver from the System properties - setDriver (System.getProperty ("sax.driver", "uk.co.wilson.xml.MinML")); + setDriver (System.getProperty ("sax.driver", "org.openxml.parser.XMLSAXParser")); } Parser parser = null; @@ -240,7 +241,10 @@ public abstract class XmlRpc extends HandlerBase { */ void writeObject (Object what, XmlWriter writer) throws XmlRpcException { writer.startElement ("value"); - if (what instanceof String) { + if (what == null) { + // try sending experimental element + writer.emptyElement ("nil"); + } else if (what instanceof String) { writer.chardata (what.toString ()); } else if (what instanceof Integer) { writer.startElement ("int"); @@ -290,11 +294,8 @@ public abstract class XmlRpc extends HandlerBase { } } writer.endElement ("struct"); - } else if (what == null) { - throw new XmlRpcException (0, "null is not supported as parameter in XML-RPC."); - } else { + } else throw new XmlRpcException (0, "Java class not supported in XML-RPC: " + what.getClass ()); - } writer.endElement ("value"); } @@ -420,6 +421,8 @@ public abstract class XmlRpc extends HandlerBase { currentValue.setType (STRUCT); else if ("array".equals (name)) currentValue.setType (ARRAY); + else if ("nil".equals (name)) + currentValue.setType (NIL); } @@ -501,7 +504,7 @@ public abstract class XmlRpc extends HandlerBase { } break; case BASE64: - value = Base64.decode (cdata.toCharArray ()); + value = Base64.decode (cdata.getBytes()); break; case STRING: value = cdata; @@ -590,10 +593,6 @@ public abstract class XmlRpc extends HandlerBase { buf.append (text); } - public void write (char c) { - buf.append (c); - } - public String toString () { return buf.toString (); } diff --git a/src/helma/xmlrpc/XmlRpcClient.java b/src/helma/xmlrpc/XmlRpcClient.java index a2e47d93..1b1bf149 100644 --- a/src/helma/xmlrpc/XmlRpcClient.java +++ b/src/helma/xmlrpc/XmlRpcClient.java @@ -1,4 +1,4 @@ -/** +/** * Copyright 1999 Hannes Wallnoefer * Implements a XML-RPC client. See http://www.xmlrpc.com/ */ @@ -200,7 +200,7 @@ public class XmlRpcClient implements XmlRpcHandler { runAsync (call.method, call.params, call.callback); call = dequeue (); } - releaseWorker (this, true); + releaseWorker (this, false); } void runAsync (String method, Vector params, AsyncCallback callback) { diff --git a/src/helma/xmlrpc/XmlRpcServer.java b/src/helma/xmlrpc/XmlRpcServer.java index 8182049c..e8d52adb 100644 --- a/src/helma/xmlrpc/XmlRpcServer.java +++ b/src/helma/xmlrpc/XmlRpcServer.java @@ -263,11 +263,7 @@ class Invoker implements XmlRpcHandler { catch (SecurityException s_e){ throw s_e; } - - // our policy is to make all public methods callable except the ones defined in java.lang.Object - if (method.getDeclaringClass () == Class.forName ("java.lang.Object")) - throw new XmlRpcException (0, "Invoker can't call methods defined in java.lang.Object"); - + // invoke Object returnValue = null; try { diff --git a/src/helma/xmlrpc/fesi/FesiRpcUtil.java b/src/helma/xmlrpc/fesi/FesiRpcUtil.java index 94fc9ed8..e46b622a 100644 --- a/src/helma/xmlrpc/fesi/FesiRpcUtil.java +++ b/src/helma/xmlrpc/fesi/FesiRpcUtil.java @@ -5,8 +5,6 @@ package helma.xmlrpc.fesi; import helma.xmlrpc.*; -import helma.scripting.fesi.ESNode; -import helma.objectmodel.INode; import FESI.Exceptions.*; import FESI.Data.*; @@ -16,9 +14,7 @@ import java.util.*; public class FesiRpcUtil { - /** - * convert a generic Java object to a JavaScript Object. - */ + // convert a generic Java object to a JavaScript Object. public static ESValue convertJ2E (Object what, Evaluator evaluator) throws Exception { if (what == null) return ESNull.theNull; @@ -26,7 +22,7 @@ public class FesiRpcUtil { Vector v = (Vector) what; ArrayPrototype retval = new ArrayPrototype (evaluator.getArrayPrototype (), evaluator); int l = v.size (); - for (int i=0; i 0 && args.length < 3) { - url = args[0]; - XmlRpc.setKeepAlive (true); - if (args.length == 2) - XmlRpc.setDriver (args[1]); - new AsyncBenchmark (); - } else { - System.err.println ("Usage: java helma.xmlrpc.Benchmark URL [SAXDriver]"); - } - } - - class Callback implements AsyncCallback { - - - int n; - - public Callback (Integer n) { - this.n = Math.abs (n.intValue()); - } - - public synchronized void handleResult (Object result, URL url, String method) { - if (n == ((Integer) result).intValue ()) - gCalls += 1; - else - gErrors += 1; - if (gCalls + gErrors >= clients*loops) - printStats (); - } - - public synchronized void handleError (Exception exception, URL url, String method) { - System.err.println (exception); - exception.printStackTrace (); - gErrors += 1; - if (gCalls + gErrors >= clients*loops) - printStats (); - } - - public void printStats () { - System.err.println (""); - System.err.println (gCalls+" calls, "+gErrors+" errors in "+(System.currentTimeMillis()-start)+" millis"); - System.err.println ((1000*(gCalls+gErrors)/(System.currentTimeMillis()-start))+" calls per second"); - } - -} - - -} diff --git a/src/helma/xmlrpc/test/TestBase64.java b/src/helma/xmlrpc/test/TestBase64.java deleted file mode 100644 index dc885d86..00000000 --- a/src/helma/xmlrpc/test/TestBase64.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 1999 Hannes Wallnoefer - */ - -package helma.xmlrpc.test; - -import helma.xmlrpc.*; -import java.util.*; -import java.io.IOException; - -public class TestBase64 implements Runnable { - - XmlRpcClient client; - static String url; - static int clients = 1; // 6; - static int loops = 1; //00; - - int gCalls = 0, gErrors = 0; - - byte[] data; - - public TestBase64 () throws Exception { - client = new XmlRpcClientLite (url); - - Vector args = new Vector (); - // Some JITs (Symantec, IBM) have problems with several Threads - // starting all at the same time. - // This initial XML-RPC call seems to pacify them. - args.addElement (new Integer (123)); - client.execute ("math.abs", args); - - data = new byte[20000]; - for (int j=0; j 0 && args.length < 3) { - url = args[0]; - XmlRpc.setKeepAlive (true); - // XmlRpc.setDebug (true); - if (args.length == 2) - XmlRpc.setDriver (args[1]); - new TestBase64 (); - } else { - System.err.println ("Usage: java helma.xmlrpc.Benchmark URL [SAXDriver]"); - } - } - -}