* Implement code injection feature to RhinoEngine/Core to allow
app.addRepository() to immediately compile new resources. (merge from helma_1_5 branch)
This commit is contained in:
parent
58e9431da1
commit
db45092717
10 changed files with 174 additions and 75 deletions
|
@ -19,10 +19,7 @@ package helma.framework.core;
|
|||
import helma.extensions.ConfigurationException;
|
||||
import helma.extensions.HelmaExtension;
|
||||
import helma.framework.*;
|
||||
import helma.framework.repository.ResourceComparator;
|
||||
import helma.framework.repository.Repository;
|
||||
import helma.framework.repository.FileResource;
|
||||
import helma.framework.repository.FileRepository;
|
||||
import helma.framework.repository.*;
|
||||
import helma.main.Server;
|
||||
import helma.objectmodel.*;
|
||||
import helma.objectmodel.db.*;
|
||||
|
@ -37,7 +34,7 @@ import java.util.ArrayList;
|
|||
|
||||
|
||||
/**
|
||||
* The central class of a Helma application. This class keeps a pool of so-called
|
||||
* The central class of a Helma application. This class keeps a pool of
|
||||
* request evaluators (threads with JavaScript interpreters), waits for
|
||||
* requests from the Web server or XML-RPC port and dispatches them to
|
||||
* the evaluators.
|
||||
|
@ -167,6 +164,8 @@ public final class Application implements IPathElement, Runnable {
|
|||
|
||||
private ResourceComparator resourceComparator;
|
||||
|
||||
private Resource currentCodeResource;
|
||||
|
||||
/**
|
||||
* Simple constructor for dead application instances.
|
||||
*/
|
||||
|
@ -1768,6 +1767,28 @@ public final class Application implements IPathElement, Runnable {
|
|||
return Collections.unmodifiableList(repositories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the code resource currently being evaluated/compiled. This is used
|
||||
* to set the proper parent repository when a new repository is added
|
||||
* via app.addRepository().
|
||||
*
|
||||
* @param resource the resource being currently evaluated/compiled
|
||||
*/
|
||||
public void setCurrentCodeResource(Resource resource) {
|
||||
currentCodeResource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the code resource currently being evaluated/compiled. This is used
|
||||
* to set the proper parent repository when a new repository is added
|
||||
* via app.addRepository().
|
||||
|
||||
* @return the resource being currently evaluated/compiled
|
||||
*/
|
||||
public Resource getCurrentCodeResource() {
|
||||
return currentCodeResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the directory of the Helma server
|
||||
*/
|
||||
|
|
|
@ -21,13 +21,11 @@ import helma.objectmodel.db.DbSource;
|
|||
import helma.util.CronJob;
|
||||
import helma.util.SystemMap;
|
||||
import helma.util.WrappedMap;
|
||||
import helma.framework.repository.Repository;
|
||||
import helma.framework.repository.FileRepository;
|
||||
import helma.framework.repository.SingleFileRepository;
|
||||
import helma.framework.repository.ZipRepository;
|
||||
import helma.framework.repository.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
@ -137,6 +135,9 @@ public class ApplicationBean implements Serializable {
|
|||
* @param obj the repository, relative or absolute path to the library.
|
||||
*/
|
||||
public void addRepository(Object obj) {
|
||||
Resource current = app.getCurrentCodeResource();
|
||||
Repository parent = current == null ?
|
||||
null : current.getRepository().getRootRepository();
|
||||
Repository rep;
|
||||
if (obj instanceof String) {
|
||||
String path = (String) obj;
|
||||
|
@ -151,12 +152,12 @@ public class ApplicationBean implements Serializable {
|
|||
throw new RuntimeException("Repository path does not exist: " + obj);
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
rep = new FileRepository(file);
|
||||
rep = new FileRepository(file, parent);
|
||||
} else if (file.isFile()) {
|
||||
if (file.getName().endsWith(".zip")) {
|
||||
rep = new ZipRepository(file);
|
||||
rep = new ZipRepository(file, parent);
|
||||
} else {
|
||||
rep = new SingleFileRepository(file);
|
||||
rep = new SingleFileRepository(file, parent);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unrecognized file type in addRepository: " + obj);
|
||||
|
@ -167,6 +168,11 @@ public class ApplicationBean implements Serializable {
|
|||
throw new RuntimeException("Invalid argument to addRepository: " + obj);
|
||||
}
|
||||
app.addRepository(rep);
|
||||
try {
|
||||
app.typemgr.checkRepository(rep, true);
|
||||
} catch (IOException iox) {
|
||||
getLogger().error("Error checking repository " + rep, iox);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@ import helma.framework.repository.Resource;
|
|||
import helma.framework.repository.Repository;
|
||||
import helma.framework.repository.ResourceTracker;
|
||||
import helma.framework.repository.FileResource;
|
||||
import helma.scripting.ScriptingEngine;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
@ -112,11 +113,20 @@ public final class Prototype {
|
|||
/**
|
||||
* Adds an repository to the list of repositories
|
||||
* @param repository repository to add
|
||||
* @param update indicates whether to immediately update the prototype with the new code
|
||||
* @throws IOException if reading/updating from the repository fails
|
||||
*/
|
||||
public void addRepository(Repository repository) {
|
||||
public void addRepository(Repository repository, boolean update) throws IOException {
|
||||
if (!repositories.contains(repository)) {
|
||||
repositories.add(repository);
|
||||
props.addResource(repository.getResource("type.properties"));
|
||||
if (update) {
|
||||
RequestEvaluator eval = app.getCurrentRequestEvaluator();
|
||||
Iterator it = repository.getAllResources().iterator();
|
||||
while (it.hasNext()) {
|
||||
checkResource((Resource) it.next(), eval.scriptingEngine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,21 +167,7 @@ public final class Prototype {
|
|||
Resource[] resources = getResources();
|
||||
|
||||
for (int i = 0; i < resources.length; i++) {
|
||||
String name = resources[i].getName();
|
||||
if (!trackers.containsKey(name)) {
|
||||
if (name.endsWith(TypeManager.templateExtension) ||
|
||||
name.endsWith(TypeManager.scriptExtension) ||
|
||||
name.endsWith(TypeManager.actionExtension) ||
|
||||
name.endsWith(TypeManager.skinExtension)) {
|
||||
updatedResources = true;
|
||||
if (name.endsWith(TypeManager.skinExtension)) {
|
||||
skins.add(resources[i]);
|
||||
} else {
|
||||
code.add(resources[i]);
|
||||
}
|
||||
trackers.put(resources[i].getName(), new ResourceTracker(resources[i]));
|
||||
}
|
||||
}
|
||||
updatedResources |= checkResource(resources[i], null);
|
||||
}
|
||||
|
||||
if (updatedResources) {
|
||||
|
@ -181,6 +177,28 @@ public final class Prototype {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean checkResource(Resource res, ScriptingEngine engine) {
|
||||
String name = res.getName();
|
||||
boolean updated = false;
|
||||
if (!trackers.containsKey(name)) {
|
||||
if (name.endsWith(TypeManager.templateExtension) ||
|
||||
name.endsWith(TypeManager.scriptExtension) ||
|
||||
name.endsWith(TypeManager.actionExtension) ||
|
||||
name.endsWith(TypeManager.skinExtension)) {
|
||||
updated = true;
|
||||
if (name.endsWith(TypeManager.skinExtension)) {
|
||||
skins.add(res);
|
||||
} else {
|
||||
if (engine != null) {
|
||||
engine.injectCodeResource(lowerCaseName, res);
|
||||
}
|
||||
code.add(res);
|
||||
}
|
||||
trackers.put(res.getName(), new ResourceTracker(res));
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of resources in this prototype's repositories. Used
|
||||
|
@ -358,15 +376,21 @@ public final class Prototype {
|
|||
* Return an iterator over this prototype's code resoruces. Synchronized
|
||||
* to not return a collection in a transient state where it is just being
|
||||
* updated by the type manager.
|
||||
*
|
||||
* @return an iterator of this prototype's code resources
|
||||
*/
|
||||
public synchronized Iterator getCodeResources() {
|
||||
return code.iterator();
|
||||
// we copy over to a new list, because the underlying set may grow
|
||||
// during compilation through use of app.addRepository()
|
||||
return new ArrayList(code).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an iterator over this prototype's skin resoruces. Synchronized
|
||||
* to not return a collection in a transient state where it is just being
|
||||
* updated by the type manager.
|
||||
*
|
||||
* @return an iterator over this prototype's skin resources
|
||||
*/
|
||||
public Iterator getSkinResources() {
|
||||
return skins.iterator();
|
||||
|
|
|
@ -126,7 +126,7 @@ public final class TypeManager {
|
|||
lastCheck = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private void checkRepository(Repository repository) throws IOException {
|
||||
protected void checkRepository(Repository repository, boolean update) throws IOException {
|
||||
Repository[] list = repository.getRepositories();
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
|
||||
|
@ -143,7 +143,7 @@ public final class TypeManager {
|
|||
// this is an embedded top-level script repository
|
||||
if (app.addRepository(list[i])) {
|
||||
// repository is new, check it
|
||||
checkRepository(list[i]);
|
||||
checkRepository(list[i], update);
|
||||
}
|
||||
} else {
|
||||
// it's an prototype
|
||||
|
@ -156,7 +156,7 @@ public final class TypeManager {
|
|||
if (isValidTypeName(name))
|
||||
createPrototype(name, list[i]);
|
||||
} else {
|
||||
proto.addRepository(list[i]);
|
||||
proto.addRepository(list[i], update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ public final class TypeManager {
|
|||
((Long) lastRepoScan.get(repository)).longValue() : 0;
|
||||
if (repository.lastModified() != lastScan) {
|
||||
lastRepoScan.put(repository, new Long(repository.lastModified()));
|
||||
checkRepository(repository);
|
||||
checkRepository(repository, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,9 +59,9 @@ public class FileRepository extends AbstractRepository {
|
|||
* Constructs a FileRepository using the given directory and top-level
|
||||
* repository
|
||||
* @param dir directory
|
||||
* @param parent top-level repository
|
||||
* @param parent the parent repository, or null
|
||||
*/
|
||||
protected FileRepository(File dir, FileRepository parent) {
|
||||
public FileRepository(File dir, Repository parent) {
|
||||
// make sure our directory has an absolute path,
|
||||
// see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4117557
|
||||
if (dir.isAbsolute()) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.util.LinkedList;
|
|||
public class SingleFileRepository implements Repository {
|
||||
|
||||
final Resource res;
|
||||
final Repository parent;
|
||||
final Repository[] repositories;
|
||||
final LinkedList resources = new LinkedList();
|
||||
final LinkedList allResources = new LinkedList();
|
||||
|
@ -35,7 +36,7 @@ public class SingleFileRepository implements Repository {
|
|||
* @param initArgs absolute path to the script file
|
||||
*/
|
||||
public SingleFileRepository(String initArgs) {
|
||||
this(new File(initArgs));
|
||||
this(new File(initArgs), null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,6 +44,16 @@ public class SingleFileRepository implements Repository {
|
|||
* @param file the script file
|
||||
*/
|
||||
public SingleFileRepository(File file) {
|
||||
this(file, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SingleFileRepository using the given argument
|
||||
* @param file the script file
|
||||
* @param parent the parent repository, or null
|
||||
*/
|
||||
public SingleFileRepository(File file, Repository parent) {
|
||||
this.parent = parent;
|
||||
res = new FileResource(file, this);
|
||||
allResources.add(res);
|
||||
isScriptFile = file.getName().endsWith(".js");
|
||||
|
@ -101,7 +112,7 @@ public class SingleFileRepository implements Repository {
|
|||
* @return the parent repository
|
||||
*/
|
||||
public Repository getParentRepository() {
|
||||
return null;
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,8 +56,9 @@ public final class ZipRepository extends AbstractRepository {
|
|||
* Constructs a ZipRepository using the given zip file as top-level
|
||||
* repository
|
||||
* @param file a zip file
|
||||
* @param parent the parent repository, or null
|
||||
*/
|
||||
protected ZipRepository(File file, Repository parent) {
|
||||
public ZipRepository(File file, Repository parent) {
|
||||
this(file, parent, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package helma.scripting;
|
||||
|
||||
import helma.framework.IPathElement;
|
||||
import helma.framework.repository.Resource;
|
||||
import helma.framework.core.Application;
|
||||
import helma.framework.core.RequestEvaluator;
|
||||
import java.io.OutputStream;
|
||||
|
@ -143,4 +144,12 @@ public interface ScriptingEngine {
|
|||
* @throws IOException
|
||||
*/
|
||||
public Object deserialize(InputStream in) throws IOException, ClassNotFoundException;
|
||||
|
||||
/**
|
||||
* Add a code resource to a given prototype by immediately compiling and evaluating it.
|
||||
*
|
||||
* @param typename the type this resource belongs to
|
||||
* @param resource a code resource
|
||||
*/
|
||||
public void injectCodeResource(String typename, Resource resource);
|
||||
}
|
||||
|
|
|
@ -84,7 +84,13 @@ public final class RhinoCore implements ScopeProvider {
|
|||
this.app = app;
|
||||
wrappercache = new WeakCacheMap(500);
|
||||
prototypes = new Hashtable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the evaluator, making sure the minimum type information
|
||||
* necessary to bootstrap the rest is parsed.
|
||||
*/
|
||||
protected synchronized void initialize() {
|
||||
Context context = Context.enter();
|
||||
|
||||
context.setCompileFunctionsWithDynamicScope(true);
|
||||
|
@ -144,12 +150,22 @@ public final class RhinoCore implements ScopeProvider {
|
|||
Scriptable numberProto = ScriptableObject.getClassPrototype(global, "Number");
|
||||
numberProto.put("format", numberProto, new NumberFormat());
|
||||
|
||||
initialize();
|
||||
Collection protos = app.getPrototypes();
|
||||
for (Iterator i = protos.iterator(); i.hasNext();) {
|
||||
Prototype proto = (Prototype) i.next();
|
||||
initPrototype(proto);
|
||||
}
|
||||
|
||||
// always fully initialize global prototype, because
|
||||
// we always need it and there's no chance to trigger
|
||||
// creation on demand.
|
||||
getPrototype("global");
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Cannot initialize interpreter");
|
||||
System.err.println("Error: " + e);
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e.getMessage());
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
|
@ -172,25 +188,6 @@ public final class RhinoCore implements ScopeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the evaluator, making sure the minimum type information
|
||||
* necessary to bootstrap the rest is parsed.
|
||||
*/
|
||||
private synchronized void initialize() {
|
||||
Collection protos = app.getPrototypes();
|
||||
|
||||
for (Iterator i = protos.iterator(); i.hasNext();) {
|
||||
Prototype proto = (Prototype) i.next();
|
||||
|
||||
initPrototype(proto);
|
||||
}
|
||||
|
||||
// always fully initialize global prototype, because
|
||||
// we always need it and there's no chance to trigger
|
||||
// creation on demand.
|
||||
getPrototype("global");
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a prototype info without compiling its script files.
|
||||
*
|
||||
|
@ -763,6 +760,19 @@ public final class RhinoCore implements ScopeProvider {
|
|||
return param;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a code resource to a given prototype by immediately compiling and evaluating it.
|
||||
*
|
||||
* @param typename the type this resource belongs to
|
||||
* @param code a code resource
|
||||
*/
|
||||
public void injectCodeResource(String typename, Resource code) {
|
||||
TypeInfo type = (TypeInfo) prototypes.get(typename.toLowerCase());
|
||||
if (type == null || type.lastUpdate == -1)
|
||||
return;
|
||||
evaluate(type, code);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// private evaluation/compilation methods
|
||||
////////////////////////////////////////////////
|
||||
|
@ -776,6 +786,9 @@ public final class RhinoCore implements ScopeProvider {
|
|||
|
||||
String sourceName = code.getName();
|
||||
Reader reader = null;
|
||||
|
||||
Resource previousCurrentResource = app.getCurrentCodeResource();
|
||||
app.setCurrentCodeResource(code);
|
||||
|
||||
try {
|
||||
Scriptable op = type.objProto;
|
||||
|
@ -809,6 +822,7 @@ public final class RhinoCore implements ScopeProvider {
|
|||
}
|
||||
// e.printStackTrace();
|
||||
} finally {
|
||||
app.setCurrentCodeResource(previousCurrentResource);
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
|
@ -877,6 +891,8 @@ public final class RhinoCore implements ScopeProvider {
|
|||
if (objProto instanceof PropertyRecorder) {
|
||||
((PropertyRecorder) objProto).startRecording();
|
||||
}
|
||||
// mark this type as updated so injectCodeResource() knows it's initialized
|
||||
lastUpdate = frameworkProto.lastCodeUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -929,8 +945,9 @@ public final class RhinoCore implements ScopeProvider {
|
|||
compiledProperties = changedProperties;
|
||||
}
|
||||
|
||||
// mark this type as updated
|
||||
lastUpdate = frameworkProto.lastCodeUpdate();
|
||||
// mark this type as updated again so it reflects
|
||||
// resources added during compilation
|
||||
// lastUpdate = frameworkProto.lastCodeUpdate();
|
||||
|
||||
// If this prototype defines a postCompile() function, call it
|
||||
Context cx = Context.getCurrentContext();
|
||||
|
|
|
@ -20,6 +20,7 @@ import helma.doc.DocApplication;
|
|||
import helma.extensions.ConfigurationException;
|
||||
import helma.extensions.HelmaExtension;
|
||||
import helma.framework.*;
|
||||
import helma.framework.repository.Resource;
|
||||
import helma.framework.core.*;
|
||||
import helma.main.Server;
|
||||
import helma.objectmodel.*;
|
||||
|
@ -40,7 +41,7 @@ import java.lang.ref.WeakReference;
|
|||
*/
|
||||
public class RhinoEngine implements ScriptingEngine {
|
||||
// map for Application to RhinoCore binding
|
||||
static Map coreMap;
|
||||
static final Map coreMap = new WeakHashMap();
|
||||
|
||||
// the application we're running in
|
||||
public Application app;
|
||||
|
@ -81,7 +82,7 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
public synchronized void init(Application app, RequestEvaluator reval) {
|
||||
this.app = app;
|
||||
this.reval = reval;
|
||||
core = getRhinoCore(app);
|
||||
initRhinoCore(app);
|
||||
context = Context.enter();
|
||||
context.setCompileFunctionsWithDynamicScope(true);
|
||||
context.setApplicationClassLoader(app.getClassLoader());
|
||||
|
@ -120,24 +121,23 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
}
|
||||
}
|
||||
|
||||
static synchronized RhinoCore getRhinoCore(Application app) {
|
||||
RhinoCore core = null;
|
||||
|
||||
if (coreMap == null) {
|
||||
coreMap = new WeakHashMap();
|
||||
} else {
|
||||
/**
|
||||
* Initialize the RhinoCore instance for this engine and application.
|
||||
* @param app the application we belong to
|
||||
*/
|
||||
private synchronized void initRhinoCore(Application app) {
|
||||
synchronized (coreMap) {
|
||||
WeakReference ref = (WeakReference) coreMap.get(app);
|
||||
if (ref != null) {
|
||||
core = (RhinoCore) ref.get();
|
||||
}
|
||||
}
|
||||
|
||||
if (core == null) {
|
||||
core = new RhinoCore(app);
|
||||
coreMap.put(app, new WeakReference(core));
|
||||
if (core == null) {
|
||||
core = new RhinoCore(app);
|
||||
core.initialize();
|
||||
coreMap.put(app, new WeakReference(core));
|
||||
}
|
||||
}
|
||||
|
||||
return core;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -537,6 +537,16 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a code resource to a given prototype by immediately compiling and evaluating it.
|
||||
*
|
||||
* @param typename the type this resource belongs to
|
||||
* @param resource a code resource
|
||||
*/
|
||||
public void injectCodeResource(String typename, Resource resource) {
|
||||
core.injectCodeResource(typename, resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the application we're running in
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue