Get prototype resources through synchronized methods instead of directly
accessing them. This fixes a bug where prototypes that were in the process of being compiled were accessed by evaluators. Pass through RedirectException if notModified() has been called on the response object.
This commit is contained in:
parent
6182b9313a
commit
a1080de978
5 changed files with 90 additions and 18 deletions
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
package helma.framework.core;
|
package helma.framework.core;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
@ -25,11 +26,11 @@ public final class Prototype {
|
||||||
final String name;
|
final String name;
|
||||||
final Application app;
|
final Application app;
|
||||||
|
|
||||||
public final HashMap templates;
|
final HashMap templates;
|
||||||
public final HashMap functions;
|
final HashMap functions;
|
||||||
public final HashMap actions;
|
final HashMap actions;
|
||||||
public final HashMap skins;
|
final HashMap skins;
|
||||||
public final HashMap updatables;
|
final HashMap updatables;
|
||||||
|
|
||||||
// lastCheck is the time the prototype's files were last checked
|
// lastCheck is the time the prototype's files were last checked
|
||||||
private long lastCheck;
|
private long lastCheck;
|
||||||
|
@ -180,7 +181,63 @@ public final class Prototype {
|
||||||
public void markChecked () {
|
public void markChecked () {
|
||||||
lastCheck = System.currentTimeMillis ();
|
lastCheck = System.currentTimeMillis ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a clone of this prototype's actions container. Synchronized
|
||||||
|
* to not return a map in a transient state where it is just being
|
||||||
|
* updated by the type manager.
|
||||||
|
*/
|
||||||
|
public synchronized Map getActions () {
|
||||||
|
return (Map) actions.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a clone of this prototype's functions container. Synchronized
|
||||||
|
* to not return a map in a transient state where it is just being
|
||||||
|
* updated by the type manager.
|
||||||
|
*/
|
||||||
|
public synchronized Map getFunctions () {
|
||||||
|
return (Map) functions.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a clone of this prototype's templates container. Synchronized
|
||||||
|
* to not return a map in a transient state where it is just being
|
||||||
|
* updated by the type manager.
|
||||||
|
*/
|
||||||
|
public synchronized Map getTemplates () {
|
||||||
|
return (Map) templates.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a clone of this prototype's skins container. Synchronized
|
||||||
|
* to not return a map in a transient state where it is just being
|
||||||
|
* updated by the type manager.
|
||||||
|
*/
|
||||||
|
public synchronized Map getSkins () {
|
||||||
|
return (Map) skins.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeUpdatable (String fileName) {
|
||||||
|
updatables.remove (fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeAction (String actionName) {
|
||||||
|
actions.remove (actionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeFunctionFile (String functionFileName) {
|
||||||
|
functions.remove (functionFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeTemplate (String templateName) {
|
||||||
|
templates.remove (templateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a string representing this prototype.
|
||||||
|
*/
|
||||||
public String toString () {
|
public String toString () {
|
||||||
return "[Prototype "+ app.getName()+"/"+name+"]";
|
return "[Prototype "+ app.getName()+"/"+name+"]";
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,9 +94,9 @@ public class ActionFile implements Updatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void remove () {
|
protected void remove () {
|
||||||
prototype.actions.remove (name);
|
prototype.removeAction (name);
|
||||||
if (file != null)
|
if (file != null)
|
||||||
prototype.updatables.remove (file.getName());
|
prototype.removeUpdatable (file.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName () {
|
public String getName () {
|
||||||
|
|
|
@ -98,8 +98,8 @@ public class FunctionFile implements Updatable {
|
||||||
|
|
||||||
|
|
||||||
void remove () {
|
void remove () {
|
||||||
prototype.functions.remove (name);
|
prototype.removeFunctionFile (name);
|
||||||
prototype.updatables.remove (file.getName());
|
prototype.removeUpdatable (file.getName());
|
||||||
|
|
||||||
// if we did not add anything to any evaluator, we're done
|
// if we did not add anything to any evaluator, we're done
|
||||||
/* if (declaredProps == null || declaredProps.size() == 0)
|
/* if (declaredProps == null || declaredProps.size() == 0)
|
||||||
|
|
|
@ -176,9 +176,9 @@ public class Template extends ActionFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void remove () {
|
protected void remove () {
|
||||||
prototype.templates.remove (name);
|
prototype.removeTemplate (name);
|
||||||
if (file != null)
|
if (file != null)
|
||||||
prototype.updatables.remove (file.getName());
|
prototype.removeUpdatable (file.getName());
|
||||||
|
|
||||||
/* Iterator evals = app.typemgr.getRegisteredRequestEvaluators ();
|
/* Iterator evals = app.typemgr.getRegisteredRequestEvaluators ();
|
||||||
while (evals.hasNext ()) {
|
while (evals.hasNext ()) {
|
||||||
|
|
|
@ -24,7 +24,6 @@ import Acme.LruHashtable;
|
||||||
/**
|
/**
|
||||||
* This is the implementation of ScriptingEnvironment for the FESI EcmaScript interpreter.
|
* This is the implementation of ScriptingEnvironment for the FESI EcmaScript interpreter.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public final class FesiEvaluator implements ScriptingEngine {
|
public final class FesiEvaluator implements ScriptingEngine {
|
||||||
|
|
||||||
// the application we're running in
|
// the application we're running in
|
||||||
|
@ -32,7 +31,7 @@ public final class FesiEvaluator implements ScriptingEngine {
|
||||||
|
|
||||||
// The FESI evaluator
|
// The FESI evaluator
|
||||||
final Evaluator evaluator;
|
final Evaluator evaluator;
|
||||||
|
|
||||||
// the global object
|
// the global object
|
||||||
final GlobalObject global;
|
final GlobalObject global;
|
||||||
|
|
||||||
|
@ -44,7 +43,7 @@ public final class FesiEvaluator implements ScriptingEngine {
|
||||||
|
|
||||||
// the request evaluator instance owning this fesi evaluator
|
// the request evaluator instance owning this fesi evaluator
|
||||||
final RequestEvaluator reval;
|
final RequestEvaluator reval;
|
||||||
|
|
||||||
// extensions loaded by this evaluator
|
// extensions loaded by this evaluator
|
||||||
static String[] extensions = new String[] {
|
static String[] extensions = new String[] {
|
||||||
"FESI.Extensions.BasicIO",
|
"FESI.Extensions.BasicIO",
|
||||||
|
@ -63,6 +62,9 @@ public final class FesiEvaluator implements ScriptingEngine {
|
||||||
// the global vars set by extensions
|
// the global vars set by extensions
|
||||||
HashMap extensionGlobals;
|
HashMap extensionGlobals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a FESI evaluator for the given application and request evaluator.
|
||||||
|
*/
|
||||||
public FesiEvaluator (Application app, RequestEvaluator reval) {
|
public FesiEvaluator (Application app, RequestEvaluator reval) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.reval = reval;
|
this.reval = reval;
|
||||||
|
@ -81,7 +83,7 @@ public final class FesiEvaluator implements ScriptingEngine {
|
||||||
Database dbx = (Database) evaluator.addExtension ("helma.scripting.fesi.extensions.Database");
|
Database dbx = (Database) evaluator.addExtension ("helma.scripting.fesi.extensions.Database");
|
||||||
dbx.setApplication (app);
|
dbx.setApplication (app);
|
||||||
// load extensions defined in server.properties:
|
// load extensions defined in server.properties:
|
||||||
extensionGlobals = new HashMap ();
|
extensionGlobals = new HashMap ();
|
||||||
Vector extVec = Server.getServer ().getExtensions ();
|
Vector extVec = Server.getServer ().getExtensions ();
|
||||||
for (int i=0; i<extVec.size(); i++ ) {
|
for (int i=0; i<extVec.size(); i++ ) {
|
||||||
HelmaExtension ext = (HelmaExtension)extVec.get(i);
|
HelmaExtension ext = (HelmaExtension)extVec.get(i);
|
||||||
|
@ -108,6 +110,10 @@ public final class FesiEvaluator implements ScriptingEngine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the evaluator, making sure the minimum type information
|
||||||
|
* necessary to bootstrap the rest is parsed.
|
||||||
|
*/
|
||||||
private void initialize () {
|
private void initialize () {
|
||||||
Collection prototypes = app.getPrototypes();
|
Collection prototypes = app.getPrototypes();
|
||||||
for (Iterator i=prototypes.iterator(); i.hasNext(); ) {
|
for (Iterator i=prototypes.iterator(); i.hasNext(); ) {
|
||||||
|
@ -120,6 +126,9 @@ public final class FesiEvaluator implements ScriptingEngine {
|
||||||
getPrototype ("global");
|
getPrototype ("global");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a prototype without fully parsing its script files.
|
||||||
|
*/
|
||||||
void initPrototype (Prototype prototype) {
|
void initPrototype (Prototype prototype) {
|
||||||
// System.err.println ("FESI INIT PROTO "+prototype);
|
// System.err.println ("FESI INIT PROTO "+prototype);
|
||||||
ObjectPrototype op = null;
|
ObjectPrototype op = null;
|
||||||
|
@ -169,6 +178,9 @@ public final class FesiEvaluator implements ScriptingEngine {
|
||||||
// evaluatePrototype (prototype);
|
// evaluatePrototype (prototype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up a prototype, parsing and compiling all its script files.
|
||||||
|
*/
|
||||||
void evaluatePrototype (Prototype prototype) {
|
void evaluatePrototype (Prototype prototype) {
|
||||||
// System.err.println ("FESI EVALUATE PROTO "+prototype+" FOR "+this);
|
// System.err.println ("FESI EVALUATE PROTO "+prototype+" FOR "+this);
|
||||||
ObjectPrototype op = null;
|
ObjectPrototype op = null;
|
||||||
|
@ -215,21 +227,21 @@ public final class FesiEvaluator implements ScriptingEngine {
|
||||||
global.putHiddenProperty (name, new NodeConstructor (name, fp, this));
|
global.putHiddenProperty (name, new NodeConstructor (name, fp, this));
|
||||||
} catch (EcmaScriptException ignore) {}
|
} catch (EcmaScriptException ignore) {}
|
||||||
}
|
}
|
||||||
for (Iterator it = prototype.functions.values().iterator(); it.hasNext(); ) {
|
for (Iterator it = prototype.getFunctions().values().iterator(); it.hasNext(); ) {
|
||||||
FunctionFile ff = (FunctionFile) it.next ();
|
FunctionFile ff = (FunctionFile) it.next ();
|
||||||
if (ff.hasFile ())
|
if (ff.hasFile ())
|
||||||
evaluateFile (prototype, ff.getFile ());
|
evaluateFile (prototype, ff.getFile ());
|
||||||
else
|
else
|
||||||
evaluateString (prototype, ff.getContent ());
|
evaluateString (prototype, ff.getContent ());
|
||||||
}
|
}
|
||||||
for (Iterator it = prototype.templates.values().iterator(); it.hasNext(); ) {
|
for (Iterator it = prototype.getTemplates().values().iterator(); it.hasNext(); ) {
|
||||||
Template tmp = (Template) it.next ();
|
Template tmp = (Template) it.next ();
|
||||||
try {
|
try {
|
||||||
FesiActionAdapter adp = new FesiActionAdapter (tmp);
|
FesiActionAdapter adp = new FesiActionAdapter (tmp);
|
||||||
adp.updateEvaluator (this);
|
adp.updateEvaluator (this);
|
||||||
} catch (EcmaScriptException ignore) {}
|
} catch (EcmaScriptException ignore) {}
|
||||||
}
|
}
|
||||||
for (Iterator it = prototype.actions.values().iterator(); it.hasNext(); ) {
|
for (Iterator it = prototype.getActions().values().iterator(); it.hasNext(); ) {
|
||||||
ActionFile act = (ActionFile) it.next ();
|
ActionFile act = (ActionFile) it.next ();
|
||||||
try {
|
try {
|
||||||
FesiActionAdapter adp = new FesiActionAdapter (act);
|
FesiActionAdapter adp = new FesiActionAdapter (act);
|
||||||
|
@ -380,6 +392,9 @@ public final class FesiEvaluator implements ScriptingEngine {
|
||||||
// into an EcmaScript exception, which is why we can't explicitly catch it
|
// into an EcmaScript exception, which is why we can't explicitly catch it
|
||||||
if (reval.res.getRedirect() != null)
|
if (reval.res.getRedirect() != null)
|
||||||
throw new RedirectException (reval.res.getRedirect ());
|
throw new RedirectException (reval.res.getRedirect ());
|
||||||
|
// do the same for not-modified responses
|
||||||
|
if (reval.res.getNotModified())
|
||||||
|
throw new RedirectException (null);
|
||||||
// create and throw a ScriptingException with the right message
|
// create and throw a ScriptingException with the right message
|
||||||
String msg = x.getMessage ();
|
String msg = x.getMessage ();
|
||||||
if (msg == null || msg.length() < 10)
|
if (msg == null || msg.length() < 10)
|
||||||
|
|
Loading…
Add table
Reference in a new issue