From bb0d9c02fdda91cd6854b1c5b154813fa4324fea Mon Sep 17 00:00:00 2001 From: hns Date: Mon, 14 May 2001 14:43:30 +0000 Subject: [PATCH] First implementation of zipped applications or zipped application parts. Not especially clean architecture. A later version should try to better abstract ordinary and zipped files. --- src/helma/framework/core/FunctionFile.java | 68 ++++++++++--- src/helma/framework/core/Prototype.java | 104 +++++++------------- src/helma/framework/core/SkinFile.java | 13 +++ src/helma/framework/core/TypeManager.java | 47 +++++++-- src/helma/framework/core/ZippedAppFile.java | 85 ++++++++++++++-- 5 files changed, 216 insertions(+), 101 deletions(-) diff --git a/src/helma/framework/core/FunctionFile.java b/src/helma/framework/core/FunctionFile.java index 05402bd2..392cb634 100644 --- a/src/helma/framework/core/FunctionFile.java +++ b/src/helma/framework/core/FunctionFile.java @@ -26,6 +26,7 @@ public class FunctionFile implements Updatable { 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 @@ -41,6 +42,32 @@ public class FunctionFile implements Updatable { 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.app; + this.name = name; + this.file = null; + this.content = body; + + Iterator evals = app.typemgr.getRegisteredRequestEvaluators (); + while (evals.hasNext ()) { + try { + + StringEvaluationSource es = new StringEvaluationSource (body, null); + StringReader reader = new StringReader (body); + + RequestEvaluator reval = (RequestEvaluator) evals.next (); + updateRequestEvaluator (reval, reader, es); + + } catch (Exception ignore) {} + } + + } /** * Tell the type manager whether we need an update. this is the case when @@ -66,7 +93,9 @@ public class FunctionFile implements Updatable { try { RequestEvaluator reval = (RequestEvaluator) evals.next (); - updateRequestEvaluator (reval); + FileReader fr = new FileReader(file); + EvaluationSource es = new FileEvaluationSource(file.getPath(), null); + updateRequestEvaluator (reval, fr, es); } catch (Exception ignore) {} } @@ -74,10 +103,22 @@ public class FunctionFile implements Updatable { } - public synchronized void updateRequestEvaluator (RequestEvaluator reval) { - EvaluationSource es = new FileEvaluationSource(file.getPath(), null); - FileReader fr = null; + public synchronized void updateRequestEvaluator (RequestEvaluator reval) { + if (file != null) { + try { + FileReader fr = new FileReader (file); + EvaluationSource es = new FileEvaluationSource (file.getPath (), null); + updateRequestEvaluator (reval, fr, es); + } catch (Exception ignore) {} + } else { + StringReader reader = new StringReader (content); + StringEvaluationSource es = new StringEvaluationSource (content, null); + updateRequestEvaluator (reval, reader, es); + } + } + + public synchronized void updateRequestEvaluator (RequestEvaluator reval, Reader reader, EvaluationSource source) { HashMap priorProps = null; HashSet newProps = null; @@ -100,8 +141,7 @@ public class FunctionFile implements Updatable { } // do the update, evaluating the file - fr = new FileReader(file); - reval.evaluator.evaluate(fr, op, es, false); + reval.evaluator.evaluate(reader, op, source, false); // check what's new if (declaredPropsTimestamp != lastmod) try { @@ -113,14 +153,13 @@ public class FunctionFile implements Updatable { } } catch (Exception ignore) {} - } catch (IOException e) { - app.logEvent ("Error parsing function file "+app.getName()+":"+prototype.getName()+"/"+file.getName()+": "+e); - } catch (EcmaScriptException e) { - app.logEvent ("Error parsing function file "+app.getName()+":"+prototype.getName()+"/"+file.getName()+": "+e); + } catch (Exception e) { + // app.logEvent ("Error parsing function file "+app.getName()+":"+prototype.getName()+"/"+file.getName()+": "+e); + app.logEvent ("Error parsing function file "+source+": "+e); } finally { - if (fr!=null) { + if (reader != null) { try { - fr.close(); + reader.close(); } catch (IOException ignore) {} } @@ -183,7 +222,10 @@ public class FunctionFile implements Updatable { } public String toString () { - return prototype.getName()+"/"+file.getName(); + if (file == null) + return "[Zipped script file]"; + else + return prototype.getName()+"/"+file.getName(); } diff --git a/src/helma/framework/core/Prototype.java b/src/helma/framework/core/Prototype.java index 506a4da1..0904f67f 100644 --- a/src/helma/framework/core/Prototype.java +++ b/src/helma/framework/core/Prototype.java @@ -26,26 +26,20 @@ public class Prototype { String name; Application app; HashMap templates, functions, actions, skins, updatables; - File codeDir; long lastUpdate; - DbMapping dbmap; + // DbMapping dbmap; Prototype prototype; - public Prototype (File codeDir, Application app) { + public Prototype (String name, Application app) { - app.logEvent ("Constructing Prototype "+app.getName()+"/"+codeDir.getName ()); + app.logEvent ("Constructing Prototype "+app.getName()+"/"+name); - this.codeDir = codeDir; this.app = app; - this.name = codeDir.getName (); + this.name = name; - File propfile = new File (codeDir, "type.properties"); - SystemProperties props = new SystemProperties (propfile.getAbsolutePath ()); - dbmap = new DbMapping (app, name, props); - lastUpdate = System.currentTimeMillis (); } @@ -123,35 +117,6 @@ public class Prototype { return null; } - public File getCodeDir () { - return codeDir; - } - - public synchronized boolean checkCodeDir () { - - boolean retval = false; - String[] list = codeDir.list (); - - for (int i=0; i lastUpdate) { - lastUpdate = System.currentTimeMillis (); - try { - app.typemgr.updatePrototype (this.name, codeDir, this); - // TypeManager.broadcaster.broadcast ("Finished update for prototype "+name+" @ "+new Date ()+"

"); - } catch (Exception x) { - app.logEvent ("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; @@ -159,40 +124,39 @@ public class Prototype { public void initRequestEvaluator (RequestEvaluator reval) { - ObjectPrototype op = null; + ObjectPrototype op = null; - // get the prototype's prototype if possible and necessary - ObjectPrototype opp = null; - if (prototype != null) - opp = reval.getPrototype (prototype.getName ()); - if (!"global".equalsIgnoreCase (name) && - !"hopobject".equalsIgnoreCase (name) && opp == null) - opp = reval.esNodePrototype; + // get the prototype's prototype if possible and necessary + ObjectPrototype opp = null; + if (prototype != null) + opp = reval.getPrototype (prototype.getName ()); + if (!"global".equalsIgnoreCase (name) && + !"hopobject".equalsIgnoreCase (name) && opp == null) + opp = reval.esNodePrototype; - if ("user".equalsIgnoreCase (name)) { - op = reval.esUserPrototype; - op.setPrototype (opp); - } else if ("global".equalsIgnoreCase (name)) - op = reval.global; - else if ("hopobject".equalsIgnoreCase (name)) - op = reval.esNodePrototype; - else { - op = new ObjectPrototype (opp, 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) {} - } + if ("user".equalsIgnoreCase (name)) { + op = reval.esUserPrototype; + op.setPrototype (opp); + } else if ("global".equalsIgnoreCase (name)) + op = reval.global; + else if ("hopobject".equalsIgnoreCase (name)) + op = reval.esNodePrototype; + else { + op = new ObjectPrototype (opp, 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 (Iterator it = functions.values().iterator(); it.hasNext(); ) { FunctionFile ff = (FunctionFile) it.next (); ff.updateRequestEvaluator (reval); diff --git a/src/helma/framework/core/SkinFile.java b/src/helma/framework/core/SkinFile.java index e2515861..8347e40f 100644 --- a/src/helma/framework/core/SkinFile.java +++ b/src/helma/framework/core/SkinFile.java @@ -30,6 +30,19 @@ 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); + } + /** * Tell the type manager whether we need an update. this is the case when * the file has been modified or deleted. diff --git a/src/helma/framework/core/TypeManager.java b/src/helma/framework/core/TypeManager.java index 39f9bfdb..31adbe94 100644 --- a/src/helma/framework/core/TypeManager.java +++ b/src/helma/framework/core/TypeManager.java @@ -80,7 +80,7 @@ public class TypeManager implements Runnable { updatePrototype (list[i], fileOrDir, proto); } else { // create new prototype - proto = new Prototype (fileOrDir, app); + proto = new Prototype (list[i], app); registerPrototype (list[i], fileOrDir, proto); prototypes.put (list[i], proto); // give logger thread a chance to tell what's going on @@ -137,11 +137,6 @@ public class TypeManager implements Runnable { typechecker = null; } - public Prototype getPrototype (String typename) { - return (Prototype) prototypes.get (typename); - } - - public void run () { while (Thread.currentThread () == typechecker) { @@ -163,6 +158,34 @@ public class TypeManager implements Runnable { } + /** + * Get a prototype defined for this application + */ + public Prototype getPrototype (String typename) { + return (Prototype) prototypes.get (typename); + } + + /** + * Get a prototype, creating it if id doesn't already exist + */ + public Prototype createPrototype (String typename) { + Prototype p = getPrototype (typename); + if (p == null) { + p = new Prototype (typename, app); + p.templates = new HashMap (); + p.functions = new HashMap (); + p.actions = new HashMap (); + p.skins = new HashMap (); + p.updatables = new HashMap (); + prototypes.put (typename, p); + } + return p; + } + + + /** + * Create a prototype from a directory containing scripts and other stuff + */ public void registerPrototype (String name, File dir, Prototype proto) { // app.logEvent ("registering prototype "+name); @@ -222,7 +245,12 @@ public class TypeManager implements Runnable { } } - updatables.put ("type.properties", proto.dbmap); + // 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; @@ -240,6 +268,9 @@ public class TypeManager implements Runnable { } + /** + * Update a prototype based on the directory which defines it. + */ public void updatePrototype (String name, File dir, Prototype proto) { // app.logEvent ("updating prototype "+name); @@ -340,9 +371,7 @@ public class TypeManager implements Runnable { app.logEvent ("Error updating "+upd+" of prototye type "+name+": "+x); } } - } - } diff --git a/src/helma/framework/core/ZippedAppFile.java b/src/helma/framework/core/ZippedAppFile.java index d0638ca2..9bd9d439 100644 --- a/src/helma/framework/core/ZippedAppFile.java +++ b/src/helma/framework/core/ZippedAppFile.java @@ -3,13 +3,13 @@ package helma.framework.core; -import java.util.HashMap; -import java.util.Iterator; +import java.util.*; +import java.util.zip.*; import java.io.*; import helma.framework.*; import helma.util.Updatable; - - +import helma.objectmodel.SystemProperties; +import helma.objectmodel.DbMapping; /** * This represents a Zip-File which may contain other Updatables for one or more prototypes. @@ -26,7 +26,7 @@ public class ZippedAppFile implements Updatable { public ZippedAppFile (File file, Application app) { this.app = app; this.file = file; - System.err.println ("CREATING ZIP FILE "+this); + // System.err.println ("CREATING ZIP FILE "+this); } @@ -46,16 +46,83 @@ public class ZippedAppFile implements Updatable { } else { - lastmod = file.lastModified (); - System.err.println ("UPDATING ZIP FILE "+this); - + ZipFile zip = null; + 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); + if (fname.endsWith (".hac")) { + String name = fname.substring (0, fname.lastIndexOf (".")); + String content = getZipEntryContent (zip, entry); + // System.err.println ("["+content+"]"); + Action act = new Action (null, name, proto); + act.update (content); + 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 (null, name, proto); + tmp.update (content); + 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); + } + } + } + } 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); + // 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); }