Allow any java object to be used as Helma object.
This commit is contained in:
parent
f0932b9e5a
commit
20955225d0
2 changed files with 170 additions and 39 deletions
|
@ -8,6 +8,7 @@ import java.lang.reflect.*;
|
|||
import java.rmi.*;
|
||||
import java.rmi.server.*;
|
||||
import helma.framework.*;
|
||||
import helma.scripting.*;
|
||||
import helma.scripting.fesi.*;
|
||||
import helma.objectmodel.*;
|
||||
import helma.objectmodel.db.*;
|
||||
|
@ -33,13 +34,28 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
|
||||
// the class name of the scripting environment implementation
|
||||
static final String scriptEnvironmentName = "helma.scripting.fesi.Environment";
|
||||
ScriptingEnvironment scriptEnv;
|
||||
|
||||
// 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;
|
||||
|
||||
private String baseURI;
|
||||
|
||||
/**
|
||||
* The type manager checks if anything in the application's prototype definitions
|
||||
* has been updated prior to each evaluation.
|
||||
*/
|
||||
public TypeManager typemgr;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
@ -69,19 +85,20 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
protected volatile long xmlrpcCount = 0;
|
||||
protected volatile long errorCount = 0;
|
||||
|
||||
// the URL-prefix to use for links into this application
|
||||
private String baseURI;
|
||||
|
||||
private DbMapping rootMapping, userRootMapping, userMapping;
|
||||
|
||||
// the root of the website, if a custom root object is defined.
|
||||
// otherwise this is managed by the NodeManager and not cached here.
|
||||
IPathElement rootObject = null;
|
||||
String rootObjectClass;
|
||||
|
||||
boolean checkSubnodes;
|
||||
// boolean checkSubnodes;
|
||||
|
||||
// name of respone encoding
|
||||
String charset;
|
||||
|
||||
// password file to use for authenticate() function
|
||||
private CryptFile pwfile;
|
||||
|
||||
// a cache for parsed skin objects
|
||||
CacheMap skincache = new CacheMap (100, 0.75f);
|
||||
|
||||
/**
|
||||
|
@ -91,11 +108,21 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
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, SystemProperties sysProps, SystemProperties sysDbProps, File home)
|
||||
public Application (String name, File home, SystemProperties sysProps, SystemProperties sysDbProps)
|
||||
throws RemoteException, IllegalArgumentException {
|
||||
|
||||
if (name == null || name.trim().length() == 0)
|
||||
|
@ -104,9 +131,14 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
this.name = name;
|
||||
this.home = home;
|
||||
|
||||
// give the Helma Thread group a name so the threads can be recognized
|
||||
threadgroup = new ThreadGroup ("TX-"+name);
|
||||
|
||||
String appHome = sysProps.getProperty ("appHome");
|
||||
// check the system props to see if custom app directory is set.
|
||||
// otherwise use <home>/apps/<appname>
|
||||
String appHome = null;
|
||||
if (sysProps != null)
|
||||
appHome = sysProps.getProperty ("appHome");
|
||||
if (appHome != null && !"".equals (appHome.trim()))
|
||||
appDir = new File (appHome);
|
||||
else
|
||||
|
@ -115,7 +147,11 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
if (!appDir.exists())
|
||||
appDir.mkdirs ();
|
||||
|
||||
String dbHome = sysProps.getProperty ("dbHome");
|
||||
// check the system props to see if custom embedded db directory is set.
|
||||
// otherwise use <home>/db/<appname>
|
||||
String dbHome = null;
|
||||
if (sysProps != null)
|
||||
dbHome = sysProps.getProperty ("dbHome");
|
||||
if (dbHome != null && !"".equals (dbHome.trim()))
|
||||
dbDir = new File (dbHome);
|
||||
else
|
||||
|
@ -124,21 +160,25 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
if (!dbDir.exists())
|
||||
dbDir.mkdirs ();
|
||||
|
||||
// create app-level properties
|
||||
File propfile = new File (appDir, "app.properties");
|
||||
props = new SystemProperties (propfile.getAbsolutePath (), 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);
|
||||
|
||||
// character encoding to be used for responses
|
||||
charset = props.getProperty ("charset", "ISO-8859-1");
|
||||
|
||||
debug = "true".equalsIgnoreCase (props.getProperty ("debug"));
|
||||
checkSubnodes = !"false".equalsIgnoreCase (props.getProperty ("subnodeChecking"));
|
||||
// checkSubnodes = !"false".equalsIgnoreCase (props.getProperty ("subnodeChecking"));
|
||||
|
||||
// get class name of root object if defined. Otherwise native Helma objectmodel will be used.
|
||||
rootObjectClass = props.getProperty ("rootObject");
|
||||
|
@ -163,7 +203,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
}
|
||||
|
||||
/**
|
||||
* Finish initializing the application
|
||||
* Get the application ready to run, initializing the evaluators and type manager.
|
||||
*/
|
||||
public void init () throws DbException {
|
||||
|
||||
|
@ -386,22 +426,43 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
nmgr.replicateCache (add, delete);
|
||||
}
|
||||
|
||||
|
||||
public void ping () {
|
||||
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 IPathElement getDataRoot () {
|
||||
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.
|
||||
// NOTE: This is but a very rough first sketch of an implementation
|
||||
// and needs much more care.
|
||||
if (rootObject == null) try {
|
||||
Class c = Class.forName (rootObjectClass);
|
||||
rootObject = (IPathElement) c.newInstance ();
|
||||
rootObject = c.newInstance ();
|
||||
} catch (Throwable x) {
|
||||
System.err.println ("ERROR CREATING ROOT OBJECT: "+x);
|
||||
}
|
||||
|
@ -449,11 +510,14 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
* Return a prototype for a given node. If the node doesn't specify a prototype,
|
||||
* return the generic hopobject prototype.
|
||||
*/
|
||||
public Prototype getPrototype (IPathElement n) {
|
||||
String protoname = n.getPrototype ();
|
||||
public Prototype getPrototype (Object obj) {
|
||||
String protoname = getPrototypeName (obj);
|
||||
if (protoname == null)
|
||||
return typemgr.getPrototype ("hopobject");
|
||||
return typemgr.getPrototype (protoname);
|
||||
Prototype p = typemgr.getPrototype (protoname);
|
||||
if (p == null)
|
||||
p = typemgr.getPrototype ("hopobject");
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -584,7 +648,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
*/
|
||||
public String getNodeHref (IPathElement elem, String actionName) {
|
||||
// FIXME: will fail for non-node roots
|
||||
IPathElement root = getDataRoot ();
|
||||
Object root = getDataRoot ();
|
||||
INode users = getUserRoot ();
|
||||
|
||||
// check base uri and optional root prototype from app.properties
|
||||
|
@ -598,26 +662,26 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
|
||||
String divider = "/";
|
||||
StringBuffer b = new StringBuffer ();
|
||||
IPathElement p = elem;
|
||||
Object p = elem;
|
||||
int loopWatch = 0;
|
||||
|
||||
while (p != null && p.getParentElement () != null && p != root) {
|
||||
while (p != null && getParentElement (p) != null && p != root) {
|
||||
|
||||
if (rootproto != null && rootproto.equals (p.getPrototype ()))
|
||||
if (rootproto != null && rootproto.equals (getPrototypeName (p)))
|
||||
break;
|
||||
|
||||
b.insert (0, divider);
|
||||
|
||||
// users always have a canonical URL like /users/username
|
||||
if ("user".equals (p.getPrototype ())) {
|
||||
b.insert (0, UrlEncoder.encode (p.getElementName ()));
|
||||
if ("user".equals (getPrototypeName (p))) {
|
||||
b.insert (0, UrlEncoder.encode (getElementName (p)));
|
||||
p = users;
|
||||
break;
|
||||
}
|
||||
|
||||
b.insert (0, UrlEncoder.encode (p.getElementName ()));
|
||||
b.insert (0, UrlEncoder.encode (getElementName (p)));
|
||||
|
||||
p = p.getParentElement ();
|
||||
p = getParentElement (p);
|
||||
|
||||
if (loopWatch++ > 20)
|
||||
break;
|
||||
|
@ -653,6 +717,58 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
return debug;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// The following methods minic the IPathElement interface. This allows as
|
||||
/// to script any Java object: If the object implements IPathElement (as does
|
||||
/// the Node class in Helma's internal objectmodel) then the corresponding
|
||||
/// method is called in the object itself. Otherwise, a corresponding script function
|
||||
/// is called on the object.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Return the name to be used to get this element from its parent
|
||||
*/
|
||||
public String getElementName (Object obj) {
|
||||
if (obj instanceof IPathElement)
|
||||
return ((IPathElement) obj).getElementName ();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a child element of this object by name.
|
||||
*/
|
||||
public Object getChildElement (Object obj, String name) {
|
||||
if (obj instanceof IPathElement)
|
||||
return ((IPathElement) obj).getChildElement (name);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent element of this object.
|
||||
*/
|
||||
public Object getParentElement (Object obj) {
|
||||
if (obj instanceof IPathElement)
|
||||
return ((IPathElement) obj).getParentElement ();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 getPrototypeName (Object obj) {
|
||||
// check if e implements the IPathElement interface
|
||||
if (obj instanceof IPathElement)
|
||||
// e implements the getPrototype() method
|
||||
return ((IPathElement) obj).getPrototype ();
|
||||
else
|
||||
// use java class name as prototype name
|
||||
return obj.getClass ().getName ();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Get the logger object for logging generic events
|
||||
|
@ -945,9 +1061,9 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IRep
|
|||
* and can be switched off by adding "subnodeChecking=false" in the app.properties file.
|
||||
* It is recommended to leave it on except you suffer severe performance problems and know what you do.
|
||||
*/
|
||||
public boolean doesSubnodeChecking () {
|
||||
/* public boolean doesSubnodeChecking () {
|
||||
return checkSubnodes;
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,6 @@ 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, currentNode;
|
||||
INode[] skinmanagers;
|
||||
|
||||
/**
|
||||
|
@ -147,7 +146,9 @@ public class RequestEvaluator implements Runnable {
|
|||
app.typemgr.initRequestEvaluator (this);
|
||||
// System.err.println ("Type check overhead: "+(System.currentTimeMillis ()-startCheck)+" millis");
|
||||
|
||||
IPathElement root, currentElement;
|
||||
// object refs to ressolve request path
|
||||
Object root, currentElement;
|
||||
|
||||
// reset skinManager
|
||||
skinmanagers = null;
|
||||
skincache.clear ();
|
||||
|
@ -279,20 +280,19 @@ public class RequestEvaluator implements Runnable {
|
|||
if (pathItems[i].length () == 0)
|
||||
continue;
|
||||
|
||||
currentElement = currentElement.getChildElement (pathItems[i]);
|
||||
currentElement = app.getChildElement (currentElement, pathItems[i]);
|
||||
|
||||
// add object to request path if suitable
|
||||
if (currentElement != null) {
|
||||
// add to reqPath array
|
||||
current = getElementWrapper (currentElement);
|
||||
reqPath.putProperty (reqPath.size(), current);
|
||||
String pt = currentElement.getPrototype ();
|
||||
String pt = app.getPrototypeName (currentElement);
|
||||
if (pt != null) {
|
||||
// if a prototype exists, add also by prototype name
|
||||
reqPath.putHiddenProperty (pt, current);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -652,7 +652,7 @@ public class RequestEvaluator implements Runnable {
|
|||
return result;
|
||||
}
|
||||
|
||||
public synchronized Object invokeFunction (IPathElement node, String functionName, Object[] args)
|
||||
public synchronized Object invokeFunction (Object node, String functionName, Object[] args)
|
||||
throws Exception {
|
||||
ESObject obj = null;
|
||||
if (node == null)
|
||||
|
@ -859,20 +859,31 @@ public class RequestEvaluator implements Runnable {
|
|||
}
|
||||
|
||||
|
||||
public ESObject getElementWrapper (IPathElement e) {
|
||||
public ESObject getElementWrapper (Object e) {
|
||||
|
||||
// check if e is an instance of a helma objectmodel node.
|
||||
if (e instanceof INode)
|
||||
return getNodeWrapper ((INode) e);
|
||||
|
||||
String protoname = e.getPrototype ();
|
||||
// Gotta find out the prototype name to use for this object...
|
||||
String prototypeName = null;
|
||||
// check if e implements the IPathElement interface
|
||||
if (e instanceof IPathElement)
|
||||
// e implements the getPrototype() method
|
||||
prototypeName = ((IPathElement) e).getPrototype ();
|
||||
else
|
||||
// use java class name as prototype name
|
||||
prototypeName = e.getClass ().getName ();
|
||||
|
||||
ObjectPrototype op = getPrototype (prototypeName);
|
||||
|
||||
ObjectPrototype op = getPrototype (protoname);
|
||||
if (op == null)
|
||||
op = esNodePrototype;
|
||||
|
||||
return new ESGenericObject (op, evaluator, e);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a script wrapper for an implemntation of helma.objectmodel.INode
|
||||
*/
|
||||
|
@ -948,6 +959,10 @@ public class RequestEvaluator implements Runnable {
|
|||
prototypes.put (protoName, op);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to use for caching skins in a Hashtable.
|
||||
* The key consists out of two strings: prototype name and skin name.
|
||||
*/
|
||||
final class SkinKey {
|
||||
|
||||
final String first, second;
|
||||
|
|
Loading…
Add table
Reference in a new issue