This commit was manufactured by cvs2svn to create tag

'v_1_2_feature_complete'.
This commit is contained in:
hns 2002-05-31 13:57:10 +00:00
parent 84f281fb77
commit e1033484c6
51 changed files with 2080 additions and 2145 deletions

View file

@ -32,8 +32,6 @@ import java.io.*;
import java.util.*;
import java.net.*;
import java.text.*;
// import Acme.Serve.servlet.*;
// import Acme.Serve.servlet.http.*;
import javax.servlet.*;
import javax.servlet.http.*;
@ -257,7 +255,7 @@ public class Serve implements ServletContext, Runnable
{
servlet.init( new ServeConfig( (ServletContext) this ) );
registry.put( urlPat, servlet );
servlets.put( servlet.getClass().getName(), servlet );
servlets.put( urlPat, servlet );
}
catch ( ServletException e )
{
@ -268,12 +266,17 @@ public class Serve implements ServletContext, Runnable
public void removeServlet( String urlPat )
{
registry.remove (urlPat);
servlets.remove (urlPat);
}
public void setDefaultServlet (Servlet servlet) {
defaultServlet = servlet;
}
public void removeDefaultServlet () {
defaultServlet = null;
}
/// Register a standard set of Servlets. These will return
// files or directory listings, and run CGI programs, much like a
// standard HTTP server.
@ -655,8 +658,10 @@ class ServeConnection implements Runnable, HttpServletRequest, HttpServletRespon
// Decode %-sequences.
reqUriPath = decode( reqUriPath );
if (reqQuery != null)
reqQuery = decode (reqQuery);
// do not decode query string, since we do that from
// helma servlet where we know more about encoding!
// 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)
@ -665,10 +670,10 @@ class ServeConnection implements Runnable, HttpServletRequest, HttpServletRespon
servlet = serve.defaultServlet;
if ( servlet != null )
runServlet( (HttpServlet) servlet );
else if ( "/".equals( reqUriPath ))
/* else if ( "/".equals( reqUriPath ))
sendRedirect (serve.props.getProperty ("rootapp", "base"));
else if ( !reqUriPath.endsWith ("/"))
sendRedirect (reqUriPath+"/");
sendRedirect (reqUriPath+"/"); */
else // Not found
sendError (404, "Not Found",
"<p>If you are looking for a specific app, try <tt>/appname</tt>.</p>"+
@ -1092,7 +1097,7 @@ class ServeConnection implements Runnable, HttpServletRequest, HttpServletRespon
// In this server, the entire path is regexp-matched against the
// servlet pattern, so there's no good way to distinguish which
// part refers to the servlet.
return null;
return reqUriPath;
}
/// Returns extra path information translated to a real path. Returns
@ -1292,8 +1297,21 @@ class ServeConnection implements Runnable, HttpServletRequest, HttpServletRespon
// type has yet been assigned, it is implicitly set to text/plain.
public String getCharacterEncoding()
{
// !!!
return null;
String contentType = getContentType ();
if (contentType == null)
return (null);
int start = contentType.indexOf("charset=");
if (start < 0)
return (null);
String encoding = contentType.substring(start + 8);
int end = encoding.indexOf(';');
if (end >= 0)
encoding = encoding.substring(0, end);
encoding = encoding.trim();
if ((encoding.length() > 2) && (encoding.startsWith("\""))
&& (encoding.endsWith("\"")))
encoding = encoding.substring(1, encoding.length() - 1);
return (encoding.trim());
}

View file

@ -48,7 +48,7 @@ public class ESWrapper extends ESObject {
private boolean asBean = false; // true if created as a bean
// A marker object never returned as a valid property !
private static ESObject noPropertyMarker = null;
private ESObject noPropertyMarker = null;
private Hashtable eventHandlers = null;
private Hashtable eventAdaptors = null;

View file

@ -0,0 +1,61 @@
package helma.framework;
import java.io.Serializable;
import java.util.Map;
import helma.framework.core.Application;
public class RequestBean implements Serializable {
RequestTrans req;
public RequestBean(RequestTrans req) {
this.req = req;
}
public Object get (String name) {
return req.get (name);
}
public boolean isGet () {
return req.isGet ();
}
public boolean isPost () {
return req.isPost ();
}
public String toString() {
return "[Request]";
}
// property related methods:
public String getaction () {
return req.action;
}
public Map getdata () {
return req.getRequestData ();
}
public long getruntime () {
return (System.currentTimeMillis() - req.startTime);
}
public String getpassword () {
return req.getPassword ();
}
public String getpath () {
return req.path;
}
public String getusername () {
return req.getUsername ();
}
}

View file

@ -0,0 +1,138 @@
package helma.framework;
import java.io.Serializable;
import java.util.Map;
import helma.framework.core.Application;
public class ResponseBean implements Serializable {
ResponseTrans res;
public ResponseBean(ResponseTrans res) {
this.res = res;
}
public void encode (Object what) {
res.encode (what);
}
public void encodeXml (Object what) {
res.encodeXml (what);
}
public void format (Object what) {
res.format (what);
}
public void pushStringBuffer () {
res.pushStringBuffer ();
}
public String popStringBuffer () {
return res.popStringBuffer ();
}
public void redirect (String url) throws RedirectException {
res.redirect (url);
}
public void reset () {
res.reset ();
}
public void setCookie (String key, String value) {
res.setCookie (key, value, -1);
}
public void setCookie (String key, String value, int days) {
res.setCookie (key, value, days);
}
public void write (Object what) {
res.write (what);
}
public void writeln (Object what) {
res.writeln (what);
}
public void writeBinary (byte[] what) {
res.writeBinary (what);
}
public String toString() {
return "[Response]";
}
// property-related methods:
public boolean getcache () {
return res.cache;
}
public void setcache (boolean cache) {
res.cache = cache;
}
public String getcharset () {
return res.charset;
}
public void setcharset (String charset) {
res.charset = charset;
}
public String getcontentType () {
return res.contentType;
}
public void setcontentType (String contentType) {
res.contentType = contentType;
}
public Map getdata () {
return res.getResponseData ();
}
public String geterror () {
return res.error;
}
public String getmessage () {
return res.message;
}
public void setmessage (String message) {
res.message = message;
}
public String getrealm () {
return res.realm;
}
public void setrealm (String realm) {
res.realm = realm;
}
public void setskinpath (Object[] arr) {
res.setTranslatedSkinpath (arr);
}
public Object[] getskinpath () {
return res.getTranslatedSkinpath ();
}
public int getstatus () {
return res.status;
}
public void setstatus (int status) {
res.status = status;
}
}

View file

@ -21,7 +21,7 @@ public class ResponseTrans implements Externalizable {
public String contentType = "text/html";
/**
* Set the charset of the response.
* Set the charset (encoding) to use for the response.
*/
public String charset;
@ -143,6 +143,15 @@ public class ResponseTrans implements Externalizable {
return b.toString ();
}
/**
* Returns the number of characters written to the response buffer so far.
*/
public int getBufferLength() {
if (buffer == null)
return 0;
return buffer.length ();
}
/**
* Append a string to the response unchanged.
*/
@ -174,6 +183,16 @@ public class ResponseTrans implements Externalizable {
buffer.append (c, start, length);
}
/**
* Insert string somewhere in the response buffer. Caller has to make sure
* that buffer exists and its length is larger than offset. str may be null, in which
* case nothing happens.
*/
public void insert (int offset, String str) {
if (str != null)
buffer.insert (offset, str);
}
/**
* Replace special characters with entities, including <, > and ", thus allowing
* no HTML tags.

View file

@ -8,12 +8,10 @@ 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;
@ -67,10 +65,9 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
boolean stopped = false;
boolean debug;
public long starttime;
long starttime;
public Hashtable sessions;
public Hashtable activeUsers;
Hashtable sessions;
Hashtable dbMappings;
Hashtable dbSources;
@ -87,7 +84,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
protected String templateExtension, scriptExtension, actionExtension, skinExtension;
// A transient node that is shared among all evaluators
protected INode appnode;
protected INode cachenode;
protected volatile long requestCount = 0;
protected volatile long xmlrpcCount = 0;
protected volatile long errorCount = 0;
@ -219,11 +216,10 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
skinExtension = ".skin";
sessions = new Hashtable ();
activeUsers = new Hashtable ();
dbMappings = new Hashtable ();
dbSources = new Hashtable ();
appnode = new TransientNode ("app");
cachenode = new TransientNode ("app");
xmlrpc = helma.main.Server.getXmlRpcServer ();
xmlrpcAccess = new XmlRpcAccess (this);
}
@ -231,7 +227,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
/**
* Get the application ready to run, initializing the evaluators and type manager.
*/
public void init () throws DbException, ScriptingException {
public void init () throws DatabaseException, ScriptingException {
scriptingEngine = new helma.scripting.fesi.FesiScriptingEnvironment ();
scriptingEngine.init (this, props);
@ -263,8 +259,11 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
String usernameField = userMapping.getNameField ();
if (usernameField == null)
usernameField = "name";
p.put ("_properties", "user."+usernameField);
p.put ("_version","1.2");
p.put ("_children", "collection(user)");
p.put ("_children.accessname", usernameField);
userRootMapping = new DbMapping (this, "__userroot__", p);
rewireDbMappings ();
nmgr = new NodeManager (this, dbDir.getAbsolutePath (), props);
@ -317,7 +316,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
// shut down node manager and embedded db
try {
nmgr.shutdown ();
} catch (DbException dbx) {
} catch (DatabaseException dbx) {
System.err.println ("Error shutting down embedded db: "+dbx);
}
@ -400,7 +399,8 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
requestCount += 1;
// get user for this request's session
User u = getUser (req.session);
Session session = checkSession (req.session);
session.touch();
ResponseTrans res = null;
RequestEvaluator ev = null;
@ -419,7 +419,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
// if attachRequest returns null this means we came too late
// and the other request was finished in the meantime
ev = getEvaluator ();
res = ev.invoke (req, u);
res = ev.invoke (req, session);
}
} catch (ApplicationStoppedException stopped) {
// let the servlet know that this application has gone to heaven
@ -442,7 +442,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
res.waitForClose ();
}
}
return res;
}
@ -531,8 +531,8 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
/**
* Return a transient node that is shared by all evaluators of this application ("app node")
*/
public INode getAppNode () {
return appnode;
public INode getCacheNode () {
return cachenode;
}
@ -571,51 +571,127 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
}
/**
* Retrurn a skin for a given object. The skin is found by determining the prototype
* Return 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 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 session currently associated with a given Hop session ID.
* Create a new session if necessary.
*/
public User getUser (String sessionID) {
if (sessionID == null)
return null;
User u = (User) sessions.get (sessionID);
if (u != null) {
u.touch ();
} else {
u = new User (sessionID, this);
sessions.put (sessionID, u);
public Session checkSession (String sessionID) {
Session session = getSession(sessionID);
if ( session==null ) {
session = new Session (sessionID, this);
sessions.put (sessionID, session);
}
return u;
return session;
}
/**
* Remove the session from the sessions-table and logout the user.
*/
public void destroySession (String sessionID) {
logoutSession (getSession (sessionID));
sessions.remove (sessionID);
}
/**
* Remove the session from the sessions-table and logout the user.
*/
public void destroySession (Session session) {
logoutSession (session);
sessions.remove (session.getSessionID ());
}
/**
* Return the whole session map. We return a clone of the table to prevent
* actual changes from the table itself, which is managed by the application.
* It is safe and allowed to manipulate the session objects contained in the table, though.
*/
public Map getSessions () {
return (Map) sessions.clone ();
}
/**
* Return a list of Helma nodes (HopObjects - the database object representing the user,
* not the session object) representing currently logged in users.
*/
public List getActiveUsers () {
ArrayList list = new ArrayList();
// used to keep track of already added users - we only return
// one object per user, and users may have multiple sessions
HashSet usernames = new HashSet ();
for (Enumeration e=sessions.elements(); e.hasMoreElements(); ) {
Session s = (Session) e.nextElement ();
if(s==null) {
continue;
} else if (s.isLoggedIn() && !usernames.contains (s.getUID ()) ) {
// returns a session if it is logged in and has not been
// returned before (so for each logged-in user we get one
// session object, even if this user is logged in several
// times (used to retrieve the active users list).
INode node = s.getUserNode ();
// we check again because user may have been logged out between the first check
if (node != null) {
usernames.add (s.getUID ());
list.add(node);
}
}
}
return list;
}
/**
* Return an array of <code>SessionBean</code> objects currently associated with a given
* Helma user.
*/
public List getSessionsForUsername (String username) {
ArrayList list = new ArrayList();
if (username == null)
return list;
for (Enumeration e=sessions.elements(); e.hasMoreElements(); ) {
Session s = (Session) e.nextElement ();
if(s==null) {
continue;
} else if (username.equals (s.getUID ())) {
// append to list if session is logged in and fits the given username
list.add (new SessionBean (s));
}
}
return list;
}
/**
* Return the session currently associated with a given Hop session ID.
*/
public Session getSession (String sessionID) {
if (sessionID == null)
return null;
return (Session) sessions.get (sessionID);
}
/**
* 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)
if (uname == null)
return null;
uname = uname.toLowerCase ().trim ();
if ("".equals (uname))
return null;
INode unode = null;
try {
INode users = getUserRoot ();
unode = users.getNode (uname, false);
if (unode != null)
if (unode != null)
return null;
unode = users.createNode (uname);
unode.setPrototype ("user");
unode.setDbMapping (userMapping);
@ -628,8 +704,6 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
unode.setName (uname);
unode.setString (usernameProp, uname);
unode.setString ("password", password);
// users.setNode (uname, unode);
// return users.getNode (uname, false);
return unode;
} catch (Exception x) {
logEvent ("Error registering User: "+x);
@ -640,26 +714,23 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
/**
* 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)
public boolean loginSession (String uname, String password, Session session) {
// Check the name/password of a user and log it in to the current session
if (uname == null)
return false;
uname = uname.toLowerCase ().trim ();
if ("".equals (uname))
return false;
try {
INode users = getUserRoot ();
INode unode = users.getNode (uname, false);
Node unode = (Node)users.getNode (uname, false);
String pw = unode.getString ("password", false);
if (pw != null && pw.equals (password)) {
// give the user his/her persistant node
u.setNode (unode);
activeUsers.put (unode.getName (), u.user);
// let the old user-object forget about this session
logoutSession(session);
session.login (unode);
return true;
}
} catch (Exception x) {
return false;
}
@ -667,18 +738,10 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
}
/**
* Log out a user from this application.
* Log out a session from this application.
*/
public boolean logoutUser (ESUser u) {
if (u.user != null) {
String uid = u.user.uid;
if (uid != null)
activeUsers.remove (uid);
// switch back to the non-persistent user node as cache
u.setNode (null);
}
return true;
public void logoutSession (Session session) {
session.logout();
}
/**
@ -726,13 +789,6 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
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);
@ -740,11 +796,6 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
break;
}
if (p == users) {
b.insert (0, divider);
b.insert (0, "users");
}
if (actionName != null)
b.append (URLEncoder.encode (actionName));
@ -958,29 +1009,25 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
int sessionTimeout = 30;
try {
sessionTimeout = Math.max (0, Integer.parseInt (props.getProperty ("sessionTimeout", "30")));
} catch (Exception ignore) {}
} catch (Exception ignore) {
System.out.println(ignore.toString());
}
long now = System.currentTimeMillis ();
// check if we should clean up user sessions
if (now - lastCleanup > cleanupSleep) try {
lastCleanup = now;
// logEvent ("Cleaning up "+name+": " + sessions.size () + " sessions active");
Hashtable cloned = (Hashtable) sessions.clone ();
for (Enumeration e = cloned.elements (); e.hasMoreElements (); ) {
User u = (User) e.nextElement ();
if (now - u.lastTouched () > sessionTimeout * 60000) {
if (u.uid != null) {
try {
eval.invokeFunction (u, "onLogout", new Object[0]);
} catch (Exception ignore) {}
activeUsers.remove (u.uid);
}
sessions.remove (u.getSessionID ());
u.setNode (null);
Session session = (Session) e.nextElement ();
if (now - session.lastTouched () > sessionTimeout * 60000) {
// if (session.uid != null) {
// FIXME onlogout()! try {eval.invokeFunction (u, "onLogout", new Object[0]);} catch (Exception ignore) {}
// }
destroySession(session);
}
}
// logEvent ("Cleaned up "+name+": " + sessions.size () + " sessions remaining");
} catch (Exception cx) {
logEvent ("Error cleaning up sessions: "+cx);
cx.printStackTrace ();
@ -1127,9 +1174,9 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
return props.getProperty (propname, defvalue);
}
public SystemProperties getProperties() {
return props;
}
public SystemProperties getProperties() {
return props;
}
/**
*
@ -1189,6 +1236,10 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, IPat
return errorCount;
}
public long getStarttime () {
return starttime;
}
/**
* Periodically called to log thread stats for this application
*/

View file

@ -0,0 +1,181 @@
package helma.framework.core;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import helma.objectmodel.INode;
public class ApplicationBean implements Serializable {
Application app;
public ApplicationBean(Application app) {
this.app = app;
}
public void clearCache () {
app.clearCache ();
}
public void log (String msg) {
app.logEvent (msg);
}
public void log (String logname, String msg) {
app.getLogger (logname).log (msg);
}
public void debug (String msg) {
if (app.debug()) {
app.logEvent (msg);
}
}
public void debug (String logname, String msg) {
if (app.debug()) {
app.getLogger (logname).log (msg);
}
}
public int countSessions () {
return app.sessions.size();
}
public SessionBean getSession (String sessionID) {
if (sessionID==null)
return null;
Session session = app.getSession (sessionID.trim ());
if (session == null)
return null;
return new SessionBean (session);
}
public SessionBean createSession (String sessionID) {
if (sessionID==null)
return null;
Session session = session = app.checkSession (sessionID.trim ());
if (session == null)
return null;
return new SessionBean (session);
}
public SessionBean[] getSessions () {
SessionBean[] theArray = new SessionBean[app.sessions.size()];
int i=0;
for (Enumeration e=app.sessions.elements(); e.hasMoreElements(); ) {
SessionBean sb = new SessionBean ((Session) e.nextElement ());
theArray[i++] = sb;
}
return theArray;
}
public INode registerUser (String username, String password) {
if (username==null || password==null || "".equals (username.trim ()) || "".equals (password.trim ()) )
return null;
else
return app.registerUser (username, password);
}
public INode getUser (String username) {
if (username==null || "".equals (username.trim()) )
return null;
return app.getUserNode (username);
}
public INode[] getActiveUsers () {
List activeUsers = app.getActiveUsers ();
return (INode[]) activeUsers.toArray (new INode[0]);
}
public SessionBean[] getSessionsForUser (INode usernode) {
if (usernode==null)
return new SessionBean[0];
else
return getSessionsForUser(usernode.getName());
}
public SessionBean[] getSessionsForUser (String username) {
if (username==null || "".equals (username.trim ()) )
return new SessionBean[0];
List userSessions = app.getSessionsForUsername (username);
return (SessionBean[]) userSessions.toArray (new SessionBean[0]);
}
// getter methods for readonly properties of this application
public INode getdata() {
return app.getCacheNode ();
}
public Date getupSince () {
return new Date (app.starttime);
}
public long getrequestCount () {
return app.getRequestCount ();
}
public long getxmlrpcCount () {
return app.getXmlrpcCount ();
}
public long geterrorCount () {
return app.getErrorCount ();
}
public Application get__app__ () {
return app;
}
public Map getproperties () {
return app.getProperties ();
}
public int getfreeThreads () {
return app.countFreeEvaluators ();
}
public int getactiveThreads () {
return app.countActiveEvaluators ();
}
public int getmaxThreads () {
return app.countEvaluators ();
}
public void setmaxThreads (int n) {
// add one to the number to compensate for the internal scheduler.
app.setNumberOfEvaluators (n+1);
}
public Map getskinfiles () {
Map skinz = new Hashtable ();
for (Iterator it = app.getPrototypes().iterator(); it.hasNext(); ) {
Prototype p = (Prototype) it.next ();
Map proto = new Hashtable ();
for (Iterator it2 = p.skins.values().iterator(); it2.hasNext(); ) {
SkinFile sf = (SkinFile) it2.next ();
String name = sf.getName ();
Skin skin = sf.getSkin ();
proto.put (name, skin.getSource ());
}
skinz.put (p.getName (), proto);
}
return skinz;
}
public String toString() {
return "[Application " + app.getName() + "]";
}
}

View file

@ -35,8 +35,8 @@ public class RequestEvaluator implements Runnable {
// the method to be executed
String method;
// the user object associated with the current request
User user;
// the session object associated with the current request
Session session;
// arguments passed to the function
Object[] args;
@ -113,17 +113,18 @@ public class RequestEvaluator implements Runnable {
HashMap globals = new HashMap ();
globals.put ("root", root);
globals.put ("user", user);
globals.put ("session", session);
globals.put ("req", req);
globals.put ("res", res);
globals.put ("path", requestPath);
globals.put ("app", app.getAppNode());
globals.put ("app", app);
req.startTime = System.currentTimeMillis ();
if (error != null)
res.error = error;
if (user.message != null) {
if (session.message != null) {
// bring over the message from a redirect
res.message = user.message;
user.message = null;
res.message = session.message;
session.message = null;
}
try {
@ -224,13 +225,8 @@ public class RequestEvaluator implements Runnable {
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;
req.action = action.substring (0, action.length()-7);
// try calling onRequest() function on object before
// calling the actual action
@ -243,48 +239,14 @@ public class RequestEvaluator implements Runnable {
}
// 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);
}
app.scriptingEngine.invoke (currentElement, action, new Object[0], globals, this);
localrtx.timer.endEvent (txname+" execute");
} catch (RedirectException redirect) {
// 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;
session.message = res.message;
done = true;
}
@ -360,7 +322,7 @@ public class RequestEvaluator implements Runnable {
HashMap globals = new HashMap ();
globals.put ("root", root);
globals.put ("res", res);
globals.put ("app", app.getAppNode());
globals.put ("app", app);
currentElement = root;
@ -419,7 +381,7 @@ public class RequestEvaluator implements Runnable {
HashMap globals = new HashMap ();
globals.put ("root", root);
globals.put ("res", res);
globals.put ("app", app.getAppNode());
globals.put ("app", app);
app.scriptingEngine.invoke (thisObject, method, args, globals, this);
commitTransaction ();
@ -497,10 +459,10 @@ public class RequestEvaluator implements Runnable {
} catch (InterruptedException ir) {}
}
public synchronized ResponseTrans invoke (RequestTrans req, User user) throws Exception {
public synchronized ResponseTrans invoke (RequestTrans req, Session session) throws Exception {
this.reqtype = HTTP;
this.req = req;
this.user = user;
this.session = session;
this.res = new ResponseTrans ();
app.activeRequests.put (req, this);
@ -534,7 +496,7 @@ public class RequestEvaluator implements Runnable {
public synchronized Object invokeXmlRpc (String method, Object[] args) throws Exception {
this.reqtype = XMLRPC;
this.user = null;
this.session = null;
this.method = method;
this.args = args;
this.res = new ResponseTrans ();
@ -559,7 +521,7 @@ public class RequestEvaluator implements Runnable {
public synchronized Object invokeFunction (Object object, String functionName, Object[] args)
throws Exception {
reqtype = INTERNAL;
user = null;
session = null;
thisObject = object;
method = functionName;
this.args =args;
@ -579,10 +541,10 @@ public class RequestEvaluator implements Runnable {
return result;
}
public synchronized Object invokeFunction (User user, String functionName, Object[] args)
public synchronized Object invokeFunction (Session session, String functionName, Object[] args)
throws Exception {
reqtype = INTERNAL;
this.user = user;
this.session = session;
thisObject = null;
method = functionName;
this.args = args;
@ -650,30 +612,15 @@ public class RequestEvaluator implements Runnable {
public String getAction (Object obj, String action) {
if (obj == 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;
}
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;
}
}

View file

@ -0,0 +1,131 @@
// Session.java
package helma.framework.core;
import java.io.*;
import java.util.*;
import java.net.URLEncoder;
import helma.objectmodel.*;
import helma.objectmodel.db.*;
/**
* This represents a session currently using the Hop application.
* This comprends 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.
*/
public class Session 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 userHandle;
// the transient cache node that is exposed to javascript
// this stays the same across logins and logouts.
public TransientNode cacheNode;
long onSince, lastTouched;
// used to remember messages to the user between requests -
// used for redirects.
String message;
public Session (String sessionID, Application app) {
this.sessionID = sessionID;
this.app = app;
this.uid = null;
this.userHandle = null;
cacheNode = new TransientNode ("session");
onSince = System.currentTimeMillis ();
lastTouched = onSince;
}
/**
* attach the given user node to this session.
*/
public void login (INode usernode) {
if (usernode==null) {
userHandle = null;
uid = null;
} else {
userHandle = ((Node)usernode).getHandle();
uid = usernode.getElementName();
}
}
/**
* remove this sessions's user node.
*/
public void logout() {
userHandle = null;
uid = null;
}
public boolean isLoggedIn() {
if (userHandle!=null && uid!=null) {
return true;
} else {
return false;
}
}
/**
* Gets the user Node from this Application's NodeManager.
*/
public INode getUserNode() {
if (userHandle!=null)
return userHandle.getNode (app.getWrappedNodeManager());
else
return null;
}
/**
* Gets the transient cache node.
*/
public INode getCacheNode () {
return cacheNode;
}
public Application getApp () {
return app;
}
public String getSessionID () {
return sessionID;
}
public void touch () {
lastTouched = System.currentTimeMillis ();
}
public long lastTouched () {
return lastTouched;
}
public long onSince () {
return onSince;
}
public String toString () {
if ( uid!=null )
return "[Session for user " + uid + "]";
else
return "[Anonymous Session]";
}
/**
* 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;
}
}

View file

@ -0,0 +1,73 @@
package helma.framework.core;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import helma.objectmodel.INode;
import helma.scripting.ScriptingEnvironment;
import helma.scripting.ScriptingException;
import helma.scripting.fesi.*;
public class SessionBean implements Serializable {
// the wrapped session object
Session session;
public SessionBean(Session session) {
this.session = session;
}
public String toString() {
return session.toString ();
}
public boolean login (String username, String password) {
boolean success = session.getApp().loginSession (username, password, session);
return success;
}
public void logout () {
session.getApp().logoutSession (session);
}
public void touch () {
session.touch ();
}
public Date lastActive() {
return new Date (session.lastTouched ());
}
public Date onSince() {
return new Date (session.onSince ());
}
// property-related methods:
public INode getdata() {
return session.getCacheNode ();
}
public INode getuser() {
return session.getUserNode();
}
public String get_id () {
return session.getSessionID ();
}
public String getcookie() {
return session.getSessionID ();
}
public Date getlastActive() {
return new Date (session.lastTouched ());
}
public Date getonSince() {
return new Date (session.onSince ());
}
}

View file

@ -258,16 +258,15 @@ public class Skin {
Object handlerObject = null;
Object[] arguments = new Object[1];
arguments[0] = parameters;
// pass a clone of the parameter map so if the script changes it,
// we still keep the original version.
arguments[0] = parameters.clone ();
// flag to tell whether we found our invocation target object
boolean objectFound = true;
if (handler != null) {
if ("currentuser".equalsIgnoreCase (handler)) {
// as a special convention, we use "currentuser" to access macros in the current user object
handlerObject = reval.user.getNode ();
} else if (thisObject != null) {
if (thisObject != null) {
// not a global macro - need to find handler object
// was called with this object - check it or its parents for matching prototype
if (!handler.equalsIgnoreCase ("this") && !handler.equalsIgnoreCase (app.getPrototypeName (thisObject))) {
@ -314,6 +313,8 @@ public class Skin {
// if so, the macro evaluates to the function. Otherwise,
// a property/field with the name is used, if defined.
Object v = null;
// remember length of response buffer before calling macro
int oldLength = reval.res.getBufferLength ();
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);
@ -321,8 +322,19 @@ public class Skin {
// System.err.println ("Getting macro from property");
v = app.scriptingEngine.get (handlerObject, name, reval);
}
if (v != null)
// check if macro wrote out to response buffer
int newLength = reval.res.getBufferLength ();
if (newLength > oldLength) {
// insert prefix and append suffix
String prefix = (String) parameters.get ("prefix");
String suffix = (String) parameters.get ("suffix");
reval.res.insert (oldLength, prefix);
reval.res.write (suffix);
}
// if macro returned something append it to response
if (v != null) {
writeToResponse (v.toString (), reval.res);
}
} else {
String msg = "[HopMacro unhandled: "+getFullName()+"]";
reval.res.write (" "+msg+" ");

View file

@ -1,134 +0,0 @@
// User.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.framework.core;
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
*/
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;
long onSince, lastTouched;
// used to remember messages to the user between requests -
// used for redirects.
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);
sessionID = sid;
onSince = System.currentTimeMillis ();
lastTouched = onSince;
}
/**
* 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.
*/
public void setNode (INode n) {
// IServer.getLogger().log ("esn = "+esn);
if (n == null) {
nhandle = null;
uid = null;
} else {
uid = n.getElementName ();
nhandle = ((Node) n).getHandle ();
}
// System.err.println ("User.setNode: "+nhandle);
}
public INode getNode () {
if (nhandle == 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;
}
}
public String getSessionID () {
return sessionID;
}
public void touch () {
lastTouched = System.currentTimeMillis ();
}
public long lastTouched () {
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);
}
}

View file

@ -108,11 +108,11 @@ public class ApplicationManager {
if (server.websrv == null) {
Naming.rebind ("//:"+port+"/"+appName, app);
} else {
AcmeServletClient servlet = new AcmeServletClient (app);
if ("base".equalsIgnoreCase (appName))
boolean isRoot = "base".equalsIgnoreCase (appName);
EmbeddedServletClient servlet = new EmbeddedServletClient (appName, isRoot);
if (isRoot)
server.websrv.setDefaultServlet (servlet);
else {
// server.websrv.addServlet ("/"+appName+"/", servlet);
server.websrv.addServlet ("/"+appName+"/*", servlet);
}
}
@ -147,14 +147,17 @@ public class ApplicationManager {
}
/**
* Get an enumeration of all currently running applications.
* Get an array containing all currently running applications.
*/
public Object[] getApplications () {
return applications.values ().toArray ();
}
public Application getApplication(String name) {
return (Application)applications.get(name);
}
/**
* Get an application by name.
*/
public Application getApplication(String name) {
return (Application)applications.get(name);
}
}

View file

@ -15,7 +15,6 @@ import helma.framework.*;
import helma.framework.core.*;
import helma.xmlrpc.*;
import helma.util.*;
import com.sleepycat.db.*;
/**

View file

@ -0,0 +1,49 @@
// DatabaseException.java
package helma.objectmodel;
/**
* Thrown on any kind of Database-Error
*/
public class DatabaseException extends RuntimeException {
public DatabaseException (String msg) {
super (msg);
}
}

View file

@ -0,0 +1,35 @@
// IDatabase.java
package helma.objectmodel;
import helma.objectmodel.db.IDGenerator;
import helma.objectmodel.INode;
/**
* Interface that is implemented by Database wrappers
*/
public interface IDatabase {
// db-related
public void shutdown ();
// id-related
public String nextID() throws ObjectNotFoundException;
public IDGenerator getIDGenerator (ITransaction transaction) throws Exception;
public void saveIDGenerator (ITransaction transaction, IDGenerator idgen) throws Exception;
// node-related
public INode getNode (ITransaction transaction, String key) throws Exception;
public void saveNode (ITransaction transaction, String key, INode node) throws Exception;
public void deleteNode (ITransaction transaction, String key) throws Exception;
// transaction-related
public ITransaction beginTransaction ();
public void commitTransaction (ITransaction transaction) throws DatabaseException;
public void abortTransaction (ITransaction transaction) throws DatabaseException;
}

View file

@ -0,0 +1,18 @@
// ITransaction.java
package helma.objectmodel;
/**
* This interface is kept for databases that are able
* to run transactions. Transactions were used for the
* Berkeley database and might be used in other future
* databases, so we leave transactions in.
*/
public interface ITransaction {
}

View file

@ -26,7 +26,7 @@ public class DbMapping implements Updatable {
// prototype name of this mapping
String typename;
int version;
// int version;
// properties from where the mapping is read
SystemProperties props;
@ -135,21 +135,21 @@ public class DbMapping implements Updatable {
public synchronized void update () {
// determin file format version of type.properties file
String versionInfo = props.getProperty ("_version");
/* String versionInfo = props.getProperty ("_version");
if ("1.2".equals (versionInfo))
version = 1;
else
version = 0;
version = 0; */
table = props.getProperty (version == 0 ? "_tablename" : "_table");
table = props.getProperty ("_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");
sourceName = props.getProperty ("_db");
if (sourceName != null) {
source = app.getDbSource (sourceName);
if (source == null) {
@ -207,7 +207,7 @@ public class DbMapping implements Updatable {
Relation rel = propertyToRelation (propName);
if (rel == null)
rel = new Relation (dbField, propName, this, props);
rel.update (dbField, props, version);
rel.update (dbField, props);
p2d.put (propName, rel);
if (rel.columnName != null &&
(rel.reftype == Relation.PRIMITIVE ||
@ -223,60 +223,21 @@ public class DbMapping implements Updatable {
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;
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);
if (subnodesRel.accessor != null)
propertiesRel = subnodesRel;
} catch (Exception x) {
app.logEvent ("Error reading _subnodes relation for "+typename+": "+x.getMessage ());
// 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;
subnodesRel = propertiesRel = null;
}
if (groupbyMapping != null) {

View file

@ -1,292 +0,0 @@
// DbWrapper.java
// Copyright (c) Hannes Wallnöfer 1999-2000
package helma.objectmodel.db;
import com.sleepycat.db.*;
import helma.objectmodel.ObjectNotFoundException;
import java.io.*;
/**
* A wrapper around a Berkeley embedded database. Used to gracefully handle the case
* when the native library can not be loaded.
*/
public class DbWrapper {
private boolean loaded, useTransactions;
private Db db;
DbEnv dbenv;
final int checkpointPause = 600000; // min. 10 minutes between checkpoints
volatile long lastCheckpoint = 0;
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;
try {
dbBaseDir = new File (dbHome);
if (!dbBaseDir.exists())
dbBaseDir.mkdirs();
useTransactions = useTx;
int dbInitFlags = Db.DB_CREATE | Db.DB_THREAD | Db.DB_INIT_MPOOL;
if (useTransactions) {
dbInitFlags = dbInitFlags | Db.DB_INIT_TXN;
}
dbenv = new DbEnv (0);
try {
dbenv.open (dbHome, dbInitFlags, 0); // for berkeley 3.0, add second parameter (null)
} catch (FileNotFoundException fnf) {
// we just created the dirs, so this shouldn't happen
}
try {
dbenv.set_error_stream(System.err);
dbenv.set_errpfx("Sleepycat");
} catch (Exception e) {
System.err.println("Error in DbWrapper: "+e.toString());
}
db = new Db (dbenv, 0);
try {
db.upgrade (dbFilename, 0);
} catch (Exception ignore) {
// nothing to upgrade, db doesn't exist
}
try {
db.open (dbFilename, null, Db.DB_BTREE, Db.DB_CREATE, 0644);
} catch (FileNotFoundException fnf) {
// we just created the dirs, so this shouldn't happen
}
loaded = true;
} catch (NoClassDefFoundError noclass) {
nmgr.app.logEvent ("Warning: Using internal file based db as fallback.");
nmgr.app.logEvent ("Reason: "+noclass);
loaded = false;
} catch (UnsatisfiedLinkError nolib) {
nmgr.app.logEvent ("Warning: Using internal file based db as fallback.");
nmgr.app.logEvent ("Reason: "+nolib);
loaded = false;
}
}
public void shutdown () throws DbException {
if (loaded) {
db.close (0);
// 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");
}
}
public DbTxn beginTransaction () throws DbException {
if (loaded && useTransactions)
return dbenv.txn_begin (null, 0);
else
return null;
}
public void commitTransaction (DbTxn txn) throws DbException {
if (txn == null || !loaded || !useTransactions)
return;
txn.commit (0);
if (++txncount%100 == 0 && System.currentTimeMillis()-checkpointPause > lastCheckpoint) {
// checkpoint transaction logs in time interval specified by server.checkpointPause
// if there are more then 100 transactions to checkpoint.
checkpoint ();
}
}
public void abortTransaction (DbTxn txn) throws DbException {
if (txn == null || !loaded || !useTransactions)
return;
txn.abort ();
}
protected void checkpoint () throws DbException {
if (!loaded || !useTransactions || txncount == 0)
return;
long now = System.currentTimeMillis();
if (now - lastCheckpoint < checkpointPause)
return;
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");
}
public IDGenerator getIDGenerator (DbTxn txn, String kstr) throws Exception {
if (loaded)
return getIDGenFromDB (txn, kstr);
else
return getIDGenFromFile (kstr);
}
public Node getNode (DbTxn txn, String kstr) throws Exception {
if (loaded)
return getNodeFromDB (txn, kstr);
else
return getNodeFromFile (kstr);
}
public void save (DbTxn txn, String kstr, Object obj) throws Exception {
if (loaded)
saveToDB (txn, kstr, obj);
else
saveToFile (kstr, obj);
}
public void delete (DbTxn txn, String kstr) throws Exception {
if (loaded)
deleteFromDB (txn, kstr);
else
deleteFromFile (kstr);
}
private IDGenerator getIDGenFromDB (DbTxn txn, String kstr) throws Exception {
long now = System.currentTimeMillis ();
byte[] kbuf = kstr.getBytes ();
Dbt key = new Dbt (kbuf);
key.set_size (kbuf.length);
Dbt data = new Dbt ();
data.set_flags (Db.DB_DBT_MALLOC);
db.get (txn, key, data, 0);
byte[] b = data.get_data ();
if (b == null)
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
IDGenerator idgen = null;
ByteArrayInputStream bin = new ByteArrayInputStream (b);
ObjectInputStream oin = new ObjectInputStream (bin);
idgen = (IDGenerator) oin.readObject ();
oin.close ();
return idgen;
}
private Node getNodeFromDB (DbTxn txn, String kstr) throws Exception {
long now = System.currentTimeMillis ();
byte[] kbuf = kstr.getBytes ();
Dbt key = new Dbt (kbuf);
key.set_size (kbuf.length);
Dbt data = new Dbt ();
data.set_flags (Db.DB_DBT_MALLOC);
db.get (txn, key, data, 0);
byte[] b = data.get_data ();
if (b == null)
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
Node node = null;
ByteArrayInputStream bin = new ByteArrayInputStream (b);
ObjectInputStream oin = new ObjectInputStream (bin);
node = (Node) oin.readObject ();
oin.close ();
return node;
}
private void saveToDB (DbTxn txn, String kstr, Object obj) throws Exception {
long now = System.currentTimeMillis ();
byte kbuf[] = kstr.getBytes();
ByteArrayOutputStream bout = new ByteArrayOutputStream ();
ObjectOutputStream oout = new ObjectOutputStream (bout);
oout.writeObject (obj);
oout.close ();
byte vbuf[] = bout.toByteArray ();
Dbt key = new Dbt (kbuf);
key.set_size (kbuf.length);
Dbt value = new Dbt (vbuf);
value.set_size (vbuf.length);
db.put (txn, key, value, 0);
// nmgr.app.logEvent ("saved "+obj+", size = "+vbuf.length);
}
private void deleteFromDB (DbTxn txn, String kstr) throws Exception {
byte kbuf[] = kstr.getBytes();
Dbt key = new Dbt (kbuf);
key.set_size (kbuf.length);
db.del (txn, key, 0);
}
////////////////////////////////////////////////////////////////////////////////
// File based fallback methods
///////////////////////////////////////////////////////////////////////////////
private IDGenerator getIDGenFromFile (String kstr) throws Exception {
File f = new File (dbBaseDir, kstr);
if ( ! f.exists() )
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
IDGenerator idgen = null;
FileInputStream bin = new FileInputStream (f);
ObjectInputStream oin = new ObjectInputStream (bin);
idgen = (IDGenerator) oin.readObject ();
oin.close ();
return idgen;
}
private Node getNodeFromFile (String kstr) throws Exception {
File f = new File (dbBaseDir, kstr);
if ( ! f.exists() )
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
Node node = null;
FileInputStream bin = new FileInputStream (f);
ObjectInputStream oin = new ObjectInputStream (bin);
node = (Node) oin.readObject ();
oin.close ();
return node;
}
private void saveToFile (String kstr, Object obj) throws Exception {
File f = new File (dbBaseDir, kstr);
FileOutputStream bout = new FileOutputStream (f);
ObjectOutputStream oout = new ObjectOutputStream (bout);
oout.writeObject (obj);
oout.close ();
}
private void deleteFromFile (String kstr) throws Exception {
File f = new File (dbBaseDir, kstr);
f.delete();
}
}

View file

@ -58,7 +58,7 @@ public final class IDGenerator implements Serializable {
/**
* Get the current counter value
*/
protected long getValue () {
public long getValue () {
return counter;
}

View file

@ -124,6 +124,20 @@ public final class Node implements INode, Serializable {
out.writeObject (prototype);
}
/**
* used by Xml deserialization
*/
public void setPropMap (Hashtable propMap) {
this.propMap = propMap;
}
/**
* used by Xml deserialization
*/
public void setSubnodes (List subnodes) {
this.subnodes = subnodes;
}
private transient String prototype;
private transient NodeHandle handle;
@ -164,8 +178,9 @@ public final class Node implements INode, Serializable {
/**
* Creates a new Node with the given name. Only used by NodeManager for "root nodes" and
* not in a Transaction context, which is why we can immediately mark it as CLEAN.
* ADD: used by wrapped database to re-create an existing Node.
*/
protected Node (String name, String id, String prototype, WrappedNodeManager nmgr) {
public Node (String name, String id, String prototype, WrappedNodeManager nmgr) {
this.nmgr = nmgr;
this.id = id;
this.name = name == null || "".equals (name) ? id : name;
@ -176,6 +191,17 @@ public final class Node implements INode, Serializable {
}
/**
* Constructor used to create a Node with a given name from a wrapped database.
*/
public Node (String name, String id, String prototype, WrappedNodeManager nmgr, long created, long lastmodified) {
this (name,id,prototype,nmgr);
this.created = created;
this.lastmodified = lastmodified;
}
/**
* Constructor used for virtual nodes.
*/
@ -459,6 +485,8 @@ public final class Node implements INode, Serializable {
} else {
anonymous = true;
}
} else if (p.contains (this) > -1) {
anonymous = true;
}
} catch (Exception ignore) {
// just fall back to default method
@ -577,6 +605,10 @@ public final class Node implements INode, Serializable {
parentHandle = parent == null ? null : parent.getHandle ();
}
public void setParentHandle (NodeHandle parent) {
parentHandle = parent;
}
/**
* This version of setParent additionally marks the node as anonymous or non-anonymous,
* depending on the string argument. This is the version called from the scripting framework,
@ -1197,6 +1229,10 @@ public final class Node implements INode, Serializable {
return new Enum ();
}
public List getSubnodeList() {
return subnodes;
}
private boolean ignoreSubnodeChange () {
// return true if a change in subnodes can be ignored because it is
// stored in the subnodes themselves.
@ -1233,7 +1269,9 @@ public final class Node implements INode, Serializable {
// return propMap == null ? new Vector ().elements () : propMap.elements ();
}
public Hashtable getPropMap() {
return propMap;
}
public IProperty get (String propname, boolean inherit) {
return getProperty (propname, inherit);

View file

@ -6,7 +6,6 @@ package helma.objectmodel.db;
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.*;
@ -26,7 +25,7 @@ public final class NodeManager {
private Replicator replicator;
protected DbWrapper db;
protected IDatabase db;
protected IDGenerator idgen;
@ -42,7 +41,7 @@ public final class NodeManager {
* 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 {
public NodeManager (Application app, String dbHome, Properties props) throws DatabaseException {
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
@ -68,7 +67,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 XmlDatabase (dbHome, helma.main.Server.dbFilename, this);
initDb ();
logSql = "true".equalsIgnoreCase(props.getProperty ("logsql"));
@ -77,44 +76,44 @@ public final class NodeManager {
/**
* Method used to create the root node and id-generator, if they don't exist already.
*/
public void initDb () throws DbException {
public void initDb () throws DatabaseException {
DbTxn txn = null;
ITransaction txn = null;
try {
txn = db.beginTransaction ();
try {
idgen = db.getIDGenerator (txn, "idgen");
idgen = db.getIDGenerator (txn);
if (idgen.getValue() < idBaseValue) {
idgen.setValue (idBaseValue);
db.save (txn, "idgen", idgen);
db.saveIDGenerator (txn, idgen);
}
} catch (ObjectNotFoundException notfound) {
// will start with idBaseValue+1
idgen = new IDGenerator (idBaseValue);
db.save (txn, "idgen", idgen);
db.saveIDGenerator (txn, idgen);
}
// check if we need to set the id generator to a base value
Node node = null;
try {
node = db.getNode (txn, "0");
node = (Node)db.getNode (txn, "0");
node.nmgr = safe;
} catch (ObjectNotFoundException notfound) {
node = new Node ("root", "0", "root", safe);
node.setDbMapping (app.getDbMapping ("root"));
db.save (txn, node.getID (), node);
db.saveNode (txn, node.getID (), node);
registerNode (node); // register node with nodemanager cache
}
try {
node = db.getNode (txn, "1");
node = (Node)db.getNode (txn, "1");
node.nmgr = safe;
} catch (ObjectNotFoundException notfound) {
node = new Node ("users", "1", null, safe);
node.setDbMapping (app.getDbMapping ("__userroot__"));
db.save (txn, node.getID (), node);
db.saveNode (txn, node.getID (), node);
registerNode (node); // register node with nodemanager cache
}
@ -125,7 +124,7 @@ public final class NodeManager {
try {
db.abortTransaction (txn);
} catch (Exception ignore) {}
throw (new DbException ("Error initializing db"));
throw (new DatabaseException ("Error initializing db"));
}
}
@ -134,7 +133,7 @@ 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 {
public void shutdown () throws DatabaseException {
db.shutdown ();
if (cache != null) {
synchronized (cache) {
@ -360,7 +359,7 @@ 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 {
public void insertNode (IDatabase db, ITransaction txn, Node node) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("insertNode "+node);
@ -368,7 +367,7 @@ public final class NodeManager {
DbMapping dbm = node.getDbMapping ();
if (dbm == null || !dbm.isRelational ()) {
db.save (txn, node.getID (), node);
db.saveNode (txn, node.getID (), node);
} else {
app.logEvent ("inserting relational node: "+node.getID ());
TableDataSet tds = null;
@ -438,7 +437,7 @@ public final class NodeManager {
* 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 {
public void updateNode (IDatabase db, ITransaction txn, Node node) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("updateNode "+node);
@ -446,7 +445,7 @@ public final class NodeManager {
DbMapping dbm = node.getDbMapping ();
if (dbm == null || !dbm.isRelational ()) {
db.save (txn, node.getID (), node);
db.saveNode (txn, node.getID (), node);
} else {
TableDataSet tds = null;
@ -537,7 +536,7 @@ public final class NodeManager {
/**
* 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 {
public void deleteNode (IDatabase db, ITransaction txn, Node node) throws Exception {
Transactor tx = (Transactor) Thread.currentThread ();
// tx.timer.beginEvent ("deleteNode "+node);
@ -545,7 +544,7 @@ public final class NodeManager {
DbMapping dbm = node.getDbMapping ();
if (dbm == null || !dbm.isRelational ()) {
db.delete (txn, node.getID ());
db.deleteNode (txn, node.getID ());
} else {
Statement st = null;
try {
@ -865,14 +864,14 @@ public final class NodeManager {
// private getNode methods
///////////////////////////////////////////////////////////////////////////////////////
private Node getNodeByKey (DbTxn txn, DbKey key) throws Exception {
private Node getNodeByKey (ITransaction txn, DbKey key) throws Exception {
// Note: Key must be a DbKey, otherwise will not work for relational objects
Node node = null;
DbMapping dbm = app.getDbMapping (key.getStorageName ());
String kstr = key.getID ();
if (dbm == null || !dbm.isRelational ()) {
node = db.getNode (txn, kstr);
node = (Node)db.getNode (txn, kstr);
node.nmgr = safe;
if (node != null && dbm != null)
node.setDbMapping (dbm);
@ -905,7 +904,7 @@ public final class NodeManager {
return node;
}
private Node getNodeByRelation (DbTxn txn, Node home, String kstr, Relation rel) throws Exception {
private Node getNodeByRelation (ITransaction txn, Node home, String kstr, Relation rel) throws Exception {
Node node = null;
if (rel.virtual) {
@ -922,13 +921,13 @@ public final class NodeManager {
} else if (rel != null && rel.groupby != null) {
node = home.getGroupbySubnode (kstr, false);
if (node == null && (rel.otherType == null || !rel.otherType.isRelational ())) {
node = db.getNode (txn, kstr);
node = (Node)db.getNode (txn, kstr);
node.nmgr = safe;
}
return node;
} else if (rel == null || rel.otherType == null || !rel.otherType.isRelational ()) {
node = db.getNode (txn, kstr);
node = (Node)db.getNode (txn, kstr);
node.nmgr = safe;
node.setDbMapping (rel.otherType);
return node;

View file

@ -202,14 +202,25 @@ public final class Property implements IProperty, Serializable, Cloneable {
unregisterNode ();
if (type == JAVAOBJECT)
this.jvalue = null;
// registerNode (value);
// registerNode (value);
type = NODE;
nhandle = value.getHandle ();
dirty = true;
}
public void setNodeHandle (NodeHandle value) {
if (type == NODE)
unregisterNode ();
if (type == JAVAOBJECT)
this.jvalue = null;
// registerNode (value);
type = NODE;
nhandle = value;
dirty = true;
}
public void setJavaObjectValue (Object value) {
if (type == NODE)
unregisterNode ();

View file

@ -89,213 +89,12 @@ public class Relation {
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
// parse methods for new file format
////////////////////////////////////////////////////////////////////////////////////////////
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) {
public void update (String desc, Properties props) {
Application app = ownType.getApplication ();
if (desc == null || "".equals (desc.trim ())) {
if (propName != null) {
@ -344,7 +143,7 @@ public class Relation {
if (reftype != PRIMITIVE && reftype != INVALID) {
Vector newConstraints = new Vector ();
parseOptions_v1 (newConstraints, props);
parseOptions (newConstraints, props);
constraints = new Constraint[newConstraints.size()];
newConstraints.copyInto (constraints);
@ -352,7 +151,7 @@ public class Relation {
}
protected void parseOptions_v1 (Vector cnst, Properties props) {
protected void parseOptions (Vector cnst, Properties props) {
String loading = props.getProperty (propName+".loadmode");
aggressiveLoading = loading != null && "aggressive".equalsIgnoreCase (loading.trim());
String caching = props.getProperty (propName+".cachemode");

View file

@ -9,7 +9,6 @@ import java.sql.*;
import helma.objectmodel.*;
import helma.util.Timer;
import helma.framework.TimeoutException;
import com.sleepycat.db.*;
/**
* A subclass of thread that keeps track of changed nodes and triggers
@ -30,7 +29,7 @@ public class Transactor extends Thread {
private volatile boolean killed;
// Transaction for the embedded database
protected DbTxn txn;
protected ITransaction txn;
// Transactions for SQL data sources
protected HashMap sqlCon;
@ -108,7 +107,7 @@ public class Transactor extends Thread {
public synchronized void begin (String tnm) throws Exception {
if (killed)
throw new DbException ("Transaction started on killed thread");
throw new DatabaseException ("Transaction started on killed thread");
if (active)
abort ();
@ -171,7 +170,7 @@ public class Transactor extends Thread {
cleannodes.clear ();
if (nmgr.idgen.dirty) {
nmgr.db.save (txn, "idgen", nmgr.idgen);
nmgr.db.saveIDGenerator (txn, nmgr.idgen);
nmgr.idgen.dirty = false;
}

View file

@ -0,0 +1,95 @@
package helma.objectmodel.db;
import java.io.*;
import java.util.Date;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import helma.objectmodel.*;
import helma.objectmodel.dom.*;
/**
* A simple XML-database
*/
public class XmlDatabase implements IDatabase {
private String dbHome;
private File dbBaseDir;
private NodeManager nmgr;
private IDGenerator idgen;
// character encoding to use when writing files.
// use standard encoding by default.
private String encoding = null;
public XmlDatabase (String dbHome, String dbFilename, NodeManager nmgr) throws DatabaseException {
this.dbHome = dbHome;
this.nmgr = nmgr;
dbBaseDir = new File (dbHome);
if (!dbBaseDir.exists() && !dbBaseDir.mkdirs() )
throw new RuntimeException("Couldn't create DB-directory");
}
public void shutdown () { }
public ITransaction beginTransaction () throws DatabaseException { return null; }
public void commitTransaction (ITransaction txn) throws DatabaseException { }
public void abortTransaction (ITransaction txn) throws DatabaseException { }
public String nextID() throws ObjectNotFoundException {
if (idgen==null) {
getIDGenerator(null);
}
return idgen.newID();
}
public IDGenerator getIDGenerator (ITransaction txn) throws ObjectNotFoundException {
File file = new File (dbBaseDir, "idgen.xml");
this.idgen = IDGenParser.getIDGenerator(file);
return idgen;
}
public void saveIDGenerator (ITransaction txn, IDGenerator idgen) throws Exception {
File file = new File (dbBaseDir, "idgen.xml");
IDGenParser.saveIDGenerator(idgen,file);
this.idgen = idgen;
}
public INode getNode (ITransaction txn, String kstr) throws Exception {
File f = new File (dbBaseDir, kstr+".xml");
if ( ! f.exists() )
throw new ObjectNotFoundException ("Object not found for key "+kstr+".");
try {
XmlReader reader = new XmlReader (nmgr);
Node node = (Node)reader.read (f, null);
return node;
} catch ( RuntimeException x ) {
nmgr.app.logEvent("error reading node from XmlDatbase: " + x.toString() );
throw new ObjectNotFoundException(x.toString());
}
}
public void saveNode (ITransaction txn, String kstr, INode node) throws Exception {
XmlWriter writer = null;
File file = new File (dbBaseDir,kstr+".xml");
if (encoding != null)
writer = new XmlWriter (file, encoding);
else
writer = new XmlWriter (file);
writer.setMaxLevels(1);
boolean result = writer.write((Node)node);
writer.close();
}
public void deleteNode (ITransaction txn, String kstr) throws Exception {
File f = new File (dbBaseDir, kstr+".xml");
f.delete();
}
public void setEncoding (String enc) {
this.encoding = encoding;
}
public String getEncoding () {
return encoding;
}
}

View file

@ -0,0 +1,37 @@
package helma.objectmodel.dom;
import java.io.*;
import java.util.Date;
import org.w3c.dom.*;
import helma.objectmodel.ObjectNotFoundException;
import helma.objectmodel.db.IDGenerator;
public class IDGenParser {
public static IDGenerator getIDGenerator (File file) throws ObjectNotFoundException {
if ( ! file.exists() )
throw new ObjectNotFoundException ("IDGenerator not found in idgen.xml");
try {
Document document = XmlUtil.parse(new FileInputStream (file));
org.w3c.dom.Element tmp = (Element)document.getDocumentElement().getElementsByTagName("counter").item(0);
return new IDGenerator( Long.parseLong (XmlUtil.getTextContent(tmp)) );
} catch (Exception e) {
throw new ObjectNotFoundException(e.toString());
}
}
public static IDGenerator saveIDGenerator (IDGenerator idgen, File file) throws Exception {
OutputStreamWriter out = new OutputStreamWriter (new FileOutputStream (file));
out.write ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
out.write ("<!-- printed by helma object publisher -->\n");
out.write ("<!-- created " + (new Date()).toString() + " -->\n" );
out.write ("<xmlroot>\n");
out.write (" <counter>" + idgen.getValue() + "</counter>\n");
out.write ("</xmlroot>\n");
out.close ();
return idgen;
}
}

View file

@ -2,7 +2,7 @@ package helma.objectmodel.dom;
public interface XmlConstants {
public final String NAMESPACE = "http://www.helma.org/";
public final String NAMESPACE = "http://www.helma.org/docs/guide/features/database";
public final String DATEFORMAT = "dd.MM.yyyy HH:mm:ss z";
}

View file

@ -108,6 +108,8 @@ public class XmlConverter implements XmlConstants {
continue;
}
// FIXME: handle CDATA!
// it's some kind of element (property or child)
if ( childNode.getNodeType()==org.w3c.dom.Node.ELEMENT_NODE ) {

View file

@ -1,91 +1,95 @@
package helma.objectmodel.dom;
import java.io.*;
import java.net.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.*;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import helma.objectmodel.*;
import helma.objectmodel.INode;
import helma.objectmodel.db.DbKey;
import helma.objectmodel.db.ExternalizableVector;
import helma.objectmodel.db.Node;
import helma.objectmodel.db.NodeHandle;
import helma.objectmodel.db.NodeManager;
import helma.objectmodel.db.Property;
public class XmlReader implements XmlConstants {
private int offset = 0;
private HashMap convertedNodes;
private NodeManager nmgr = null;
public XmlReader() {
public XmlReader () {
}
public XmlReader (NodeManager nmgr) {
this.nmgr = nmgr;
}
public INode read( String desc ) {
return read(desc, new TransientNode() );
}
public INode read( String desc, INode helmaNode ) throws RuntimeException {
/**
* main entry to read an xml-file.
*/
public INode read (File file, INode helmaNode) throws RuntimeException {
try {
return read( new File(desc), helmaNode );
} catch ( FileNotFoundException notfound ) {
throw new RuntimeException( "couldn't find xml-file: " + desc );
} catch ( IOException ioerror ) {
throw new RuntimeException( "couldn't read xml: " + desc );
}
}
public INode read( File file, INode helmaNode ) throws RuntimeException, FileNotFoundException {
return read( new FileInputStream(file), helmaNode );
}
public INode read( InputStream in, INode helmaNode ) throws RuntimeException {
Document document = XmlUtil.parse(in);
if ( document!=null && document.getDocumentElement()!=null ) {
Node tmp = document.getDocumentElement().getFirstChild();
Element workelement = null;
while( tmp!=null ) {
tmp = tmp.getNextSibling();
if ( tmp.getNodeType()==Node.ELEMENT_NODE ) {
workelement = (Element) tmp;
break;
}
}
return startConversion( helmaNode, workelement );
} else {
return read (new FileInputStream(file), helmaNode);
} catch (FileNotFoundException notfound) {
System.err.println ("couldn't find xml-file: " + file.getAbsolutePath ());
return helmaNode;
}
}
public INode startConversion( INode helmaNode, Element element ) {
convertedNodes = new HashMap();
INode convertedNode = convert(helmaNode, element );
convertedNodes = null;
return convertedNode;
/**
* read an InputStream with xml-content.
*/
public INode read (InputStream in, INode helmaNode) throws RuntimeException {
if (helmaNode==null && nmgr==null)
throw new RuntimeException ("can't create a new Node without a NodeManager");
Document document = XmlUtil.parse (in);
Element element = XmlUtil.getFirstElement(document);
if (element==null)
throw new RuntimeException ("corrupted xml-file");
if (helmaNode==null) {
return convert (element);
} else {
convertedNodes = new HashMap ();
INode convertedNode = convert (element, helmaNode);
convertedNodes = null;
return convertedNode;
}
}
public INode convert( INode helmaNode, Element element ) {
offset++;
String idref = element.getAttributeNS(NAMESPACE, "idref");
String key = idref + "-" + element.getAttributeNS(NAMESPACE, "prototyperef");
/**
* convert children of an Element to a given helmaNode
*/
public INode convert (Element element, INode helmaNode) {
String idref = element.getAttribute("idref");
String key = idref + "-" + element.getAttribute("prototyperef");
if( idref!=null && !idref.equals("") ) {
if( convertedNodes.containsKey(key) ) {
offset--;
return (INode)convertedNodes.get(key);
}
}
key = element.getAttributeNS(NAMESPACE, "id") + "-" + element.getAttributeNS(NAMESPACE, "prototype");
key = element.getAttribute("id") + "-" + element.getAttribute("prototype");
convertedNodes.put( key, helmaNode );
// FIXME: import id on persistent nodes
String prototype = element.getAttributeNS(NAMESPACE, "prototype");
String prototype = element.getAttribute("prototype");
if( !prototype.equals("") && !prototype.equals("hopobject") ) {
helmaNode.setPrototype( prototype );
}
children(helmaNode, element);
offset--;
return helmaNode;
}
// used by convert(Element,INode)
private INode children( INode helmaNode, Element element ) {
NodeList list = element.getChildNodes();
int len = list.getLength();
@ -94,16 +98,23 @@ public class XmlReader implements XmlConstants {
try {
childElement = (Element)list.item(i);
} catch( ClassCastException e ) {
continue;
continue; // ignore CDATA, comments etc
}
INode workNode = null;
if ( childElement.getTagName().equals("hop:child") ) {
convert( helmaNode.createNode(null), childElement );
} else if ( !"".equals(childElement.getAttributeNS(NAMESPACE,"id")) || !"".equals(childElement.getAttributeNS(NAMESPACE,"idref")) ) {
// we've got an object!
helmaNode.setNode( childElement.getTagName(), convert( helmaNode.createNode(childElement.getTagName()), childElement ) );
convert (childElement, helmaNode.createNode(null));
} else if ( !"".equals(childElement.getAttribute("id")) || !"".equals(childElement.getAttribute("idref")) ) {
String childTagName = childElement.getTagName();
INode newNode = convert (childElement, helmaNode.createNode (childTagName));
helmaNode.setNode (childTagName, newNode);
} else {
String type = childElement.getAttribute("hop:type");
String type = childElement.getAttribute("type");
String key = childElement.getTagName();
String content = XmlUtil.getTextContent(childElement);
if ( type.equals("boolean") ) {
@ -132,26 +143,101 @@ public class XmlReader implements XmlConstants {
return helmaNode;
}
/** for testing */
public static void main ( String args[] ) throws Exception {
try {
XmlReader x = new XmlReader ();
INode node = x.read("test.xml");
} catch ( Exception e ) {
System.out.println("exception " + e.toString() );
throw new RuntimeException(e.toString());
/**
* This is a basic de-serialization method for XML-2-Node conversion.
* It reads a Node from a database-like file and should return a Node
* that matches exactly the one dumped to that file before.
* It only supports persistent-capable Nodes (from objectmodel.db-package).
*/
public helma.objectmodel.db.Node convert (Element element) {
// FIXME: this method should use Element.getAttributeNS():
// FIXME: do we need the name value or is it retrieved through mappings anyway?
String name = element.getAttribute("name");
// String name = null;
String id = element.getAttribute("id");
String prototype = element.getAttribute("prototype");
if ( "".equals(prototype) )
prototype = "hopobject";
helma.objectmodel.db.Node helmaNode = null;
try {
long created = Long.parseLong (element.getAttribute ("created"));
long lastmodified = Long.parseLong (element.getAttribute ("lastModified"));
helmaNode = new helma.objectmodel.db.Node (name,id,prototype,nmgr.safe,created,lastmodified);
} catch ( NumberFormatException e ) {
helmaNode = new helma.objectmodel.db.Node (name,id,prototype,nmgr.safe);
}
}
// now loop through all child elements and retrieve properties/subnodes for this node.
NodeList list = element.getChildNodes();
int len = list.getLength();
Hashtable propMap = new Hashtable();
List subnodes = new ExternalizableVector();
for ( int i=0; i<len; i++ ) {
/** for testing */
void debug(Object msg) {
for ( int i=0; i<offset; i++ ) {
System.out.print(" ");
Element childElement;
try {
childElement = (Element)list.item(i);
} catch( ClassCastException e ) {
continue; // ignore CDATA, comments etc
}
if ( childElement.getTagName().equals("hop:child") ) {
// add a new NodeHandle, presume all IDs in this objectcache are unique,
// a prerequisite for a simple internal database.
subnodes.add (new NodeHandle (new DbKey(null,childElement.getAttribute("idref") ) ) );
continue;
}
if ( childElement.getTagName().equals("hop:parent") ) {
// add a NodeHandle to parent object
helmaNode.setParentHandle (new NodeHandle (new DbKey(null,childElement.getAttribute("idref") ) ) );
}
// if we come until here, childelement is a property value
Property prop = new Property (childElement.getTagName(), helmaNode);
if ( !"".equals(childElement.getAttribute("id")) || !"".equals(childElement.getAttribute("idref")) ) {
// we've got an object!
String idref = childElement.getAttribute("idref");
prop.setNodeHandle (new NodeHandle(new DbKey(null,idref)));
} else {
String type = childElement.getAttribute("type");
String content = XmlUtil.getTextContent(childElement);
if ( type.equals("boolean") ) {
if ( content.equals("true") ) {
prop.setBooleanValue(true);
} else {
prop.setBooleanValue(false);
}
} else if ( type.equals("date") ) {
SimpleDateFormat format = new SimpleDateFormat ( DATEFORMAT );
try {
Date date = format.parse(content);
prop.setDateValue (date);
} catch ( ParseException e ) {
prop.setStringValue (content);
}
} else if ( type.equals("float") ) {
prop.setFloatValue ((new Double(content)).doubleValue());
} else if ( type.equals("integer") ) {
prop.setIntegerValue ((new Long(content)).longValue());
} else {
prop.setStringValue (content);
}
}
propMap.put (childElement.getTagName(), prop);
}
System.out.println(msg.toString());
if ( propMap.size()>0 )
helmaNode.setPropMap (propMap);
else
helmaNode.setPropMap (null);
if ( subnodes.size()>0 )
helmaNode.setSubnodes (subnodes);
else
helmaNode.setSubnodes (null);
return helmaNode;
}
}

View file

@ -10,6 +10,7 @@ import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
@ -54,6 +55,24 @@ public class XmlUtil {
}
}
/**
* get first "real" element (ie not the document-rootelement, but the next one
*/
public static Element getFirstElement (Document document) {
Element workelement = null;
if ( document.getDocumentElement()!=null ) {
org.w3c.dom.Node tmp = document.getDocumentElement().getFirstChild();
while( tmp!=null ) {
tmp = tmp.getNextSibling();
if ( tmp.getNodeType()==org.w3c.dom.Node.ELEMENT_NODE ) {
workelement = (Element) tmp;
break;
}
}
}
return workelement;
}
/**
* return the text content of an element
*/

View file

@ -1,65 +1,97 @@
package helma.objectmodel.dom;
import java.io.*;
import java.net.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import helma.objectmodel.*;
import helma.objectmodel.INode;
import helma.objectmodel.IProperty;
import helma.objectmodel.TransientNode;
import helma.objectmodel.db.Node;
import helma.objectmodel.db.DbMapping;
import helma.util.HtmlEncoder;
public class XmlWriter extends OutputStreamWriter implements XmlConstants {
public class XmlWriter extends OutputStreamWriter implements XmlConstants {
private final static String LINESEPARATOR = System.getProperty("line.separator");
private Vector convertedNodes;
private int maxLevels = 3;
private String indent = " ";
private StringBuffer prefix = new StringBuffer();
private static int fileid;
private SimpleDateFormat format = new SimpleDateFormat ( DATEFORMAT );
private boolean dbmode = true;
/**
* create ids that can be used for temporary files.
*/
public static int generateID() {
public static int generateID() {
return fileid++;
}
/**
* empty constructor, will use System.out as outputstream.
*/
public XmlWriter () {
public XmlWriter () {
super(System.out);
}
public XmlWriter (OutputStream out) {
public XmlWriter (OutputStream out) {
super(out);
}
public XmlWriter (String desc) throws FileNotFoundException {
public XmlWriter (OutputStream out, String enc) throws UnsupportedEncodingException {
super(out, enc);
}
public XmlWriter (String desc) throws FileNotFoundException {
super (new FileOutputStream (desc));
}
public XmlWriter (File file) throws FileNotFoundException {
public XmlWriter (String desc, String enc) throws FileNotFoundException, UnsupportedEncodingException {
super (new FileOutputStream (desc), enc);
}
public XmlWriter (File file) throws FileNotFoundException {
super (new FileOutputStream (file));
}
public XmlWriter (File file, String enc) throws FileNotFoundException, UnsupportedEncodingException {
super (new FileOutputStream (file), enc);
}
/**
* by default writing only descends 50 levels into the node tree to prevent
* infite loops. number can be changed here.
*/
public void setMaxLevels (int levels) {
public void setMaxLevels (int levels) {
maxLevels = levels;
}
public void setDatabaseMode (boolean dbmode) {
this.dbmode = dbmode;
}
/**
* set the number of space chars
*/
public void setIndent (int ct) {
public void setIndent (int ct) {
StringBuffer tmp = new StringBuffer ();
for ( int i=0; i<ct; i++ ) {
for ( int i=0; i<ct; i++ ) {
tmp.append(" ");
}
indent = tmp.toString();
@ -70,9 +102,10 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
* creates document header too and initializes
* the cache of already converted nodes.
*/
public boolean write( INode node ) throws IOException {
public boolean write( INode node ) throws IOException {
convertedNodes = new Vector();
writeln ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
String encoding = getEncoding();
writeln ("<?xml version=\"1.0\" encoding=\""+encoding+"\"?>");
writeln ("<!-- printed by helma object publisher -->");
writeln ("<!-- created " + (new Date()).toString() + " -->" );
write ("<xmlroot xmlns:hop=\"");
@ -86,17 +119,26 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
/**
* write a hopobject and print all its properties and children.
* if node has already been fully printed, just make a reference here.
* references are made here if a node already has been fully printed
* or if this is the last level that's going to be dumped
*/
public void write (INode node, String name, int level) throws IOException {
if ( ++level>maxLevels )
public void write (INode node, String name, int level) throws IOException {
if (node==null)
return;
prefix.append(indent);
if ( convertedNodes.contains(node) ) {
if ( ++level>maxLevels ) {
writeReferenceTag (node, name);
} else {
prefix = prefix.delete( prefix.length()-indent.length(), Integer.MAX_VALUE );
return;
}
if ( convertedNodes.contains(node) ) {
writeReferenceTag (node, name);
} else {
convertedNodes.addElement (node);
writeTagOpen (node,name);
if ( node.getParent()!=null ) {
writeReferenceTag (node.getParent(),"hop:parent");
}
writeProperties (node,level);
writeChildren (node,level);
writeTagClose (node,name);
@ -104,61 +146,72 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
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() ) {
private void writeProperties (INode node, int level) throws IOException {
Enumeration e = null;
if ( dbmode==true && node instanceof helma.objectmodel.db.Node ) {
// a newly constructed db.Node doesn't have a propMap,
// but returns an enumeration of all it's db-mapped properties
Hashtable props = ((Node)node).getPropMap();
if (props==null)
return;
e = props.keys();
} else {
e = node.properties();
}
while ( e.hasMoreElements() ) {
String key = (String)e.nextElement();
IProperty prop = node.get(key,false);
if ( prop!=null ) {
if ( prop!=null ) {
int type = prop.getType();
if( type==IProperty.NODE ) {
if( type==IProperty.NODE ) {
write (node.getNode(key,false), key, level);
} else {
} else {
writeProperty (node.get(key,false));
}
}
}
}
public void writeNullProperty (String key) throws IOException {
public void writeNullProperty (String key) throws IOException {
write (prefix.toString());
write (indent);
write ("<");
write (key);
write (" hop:type=\"null\"/>");
write (" type=\"null\"/>");
write (LINESEPARATOR);
}
}
/**
* write a single property, set attribute type according to type,
* write a single property, set attribute type according to type,
* apply xml-encoding.
*/
public void writeProperty (IProperty property) throws IOException {
public void writeProperty (IProperty property) throws IOException {
write (prefix.toString());
write (indent);
write ("<");
write (property.getName());
switch (property.getType()) {
switch (property.getType()) {
case IProperty.BOOLEAN:
write (" hop:type=\"boolean\"");
write (" type=\"boolean\"");
break;
case IProperty.FLOAT:
write (" hop:type=\"float\"");
write (" type=\"float\"");
break;
case IProperty.INTEGER:
write (" hop:type=\"integer\"");
case IProperty.INTEGER:
write (" type=\"integer\"");
break;
}
if ( property.getType()==IProperty.DATE ) {
write (" hop:type=\"date\"");
SimpleDateFormat format = new SimpleDateFormat ( DATEFORMAT );
if ( property.getType()==IProperty.DATE ) {
write (" type=\"date\"");
write (">");
write ( format.format (property.getDateValue()) );
} else {
} else {
write (">");
write ( HtmlEncoder.encodeXml (property.getStringValue()) );
}
@ -171,9 +224,15 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
/**
* loop through the children-array and print them as <hop:child>
*/
private void writeChildren (INode node, int level) throws IOException {
private void writeChildren (INode node, int level) throws IOException {
if ( dbmode==true && node instanceof helma.objectmodel.db.Node ) {
Node dbNode = (Node)node;
DbMapping smap = dbNode.getDbMapping() == null ? null : dbNode.getDbMapping().getSubnodeMapping ();
if (smap != null && smap.isRelational ())
return;
}
Enumeration e = node.getSubnodes();
while (e.hasMoreElements()) {
while (e.hasMoreElements()) {
INode nextNode = (INode)e.nextElement();
write (nextNode, "hop:child", level);
}
@ -183,16 +242,22 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
* 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 {
public void writeTagOpen (INode node, String name) throws IOException {
write (prefix.toString());
write ("<");
write ( (name==null)?"hopobject" : name);
write (" hop:id=\"");
write (" id=\"");
write (getNodeIdentifier(node));
write ("\" hop:prototype=\"");
write ("\" name=\"");
write (node.getName());
write ("\" prototype=\"");
write (getNodePrototype(node));
write ("\"");
write (">");
write ("\" created=\"");
write (Long.toString(node.created()));
write ("\" lastModified=\"");
write (Long.toString(node.lastModified()));
//FIXME: do we need anonymous-property?
write ("\">");
write (LINESEPARATOR);
}
@ -200,7 +265,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
* write a closing tag for a node
* e.g. </root>
*/
public void writeTagClose (INode node, String name) throws IOException {
public void writeTagClose (INode node, String name) throws IOException {
write (prefix.toString());
write ("</");
write ( (name==null)?"hopobject" : name);
@ -210,16 +275,16 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
/**
* write a tag holding a reference to an element that has
* been dumped before.
* e.g. <parent hop:idref="t35" hop:prototyperef="hopobject"/>
* been written out before.
* e.g. <parent idref="35" prototyperef="hopobject"/>
*/
public void writeReferenceTag (INode node, String name) throws IOException {
public void writeReferenceTag (INode node, String name) throws IOException {
write (prefix.toString());
write ("<");
write ( (name==null)?"hopobject" : name);
write ( " hop:idref=\"");
write ( " idref=\"");
write (getNodeIdentifier(node));
write ("\" hop:prototyperef=\"");
write ("\" prototyperef=\"");
write (getNodePrototype(node));
write ("\"");
write ("/>");
@ -229,10 +294,10 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
/**
* retrieve prototype-string of a node, defaults to "hopobject"
*/
private String getNodePrototype( INode node ) {
if ( node.getPrototype()==null || "".equals(node.getPrototype()) ) {
private String getNodePrototype( INode node ) {
if ( node.getPrototype()==null || "".equals(node.getPrototype()) ) {
return "hopobject";
} else {
} else {
return node.getPrototype();
}
}
@ -240,12 +305,12 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
/**
* 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 {
*/
private String getNodeIdentifier( INode node ) {
try {
TransientNode tmp = (TransientNode)node;
return Integer.toString( tmp.hashCode() );
} catch ( ClassCastException e ) {
} catch ( ClassCastException e ) {
return node.getID();
}
}
@ -253,7 +318,7 @@ public class XmlWriter extends OutputStreamWriter implements XmlConstants {
public void writeln(String str) throws IOException {
write (str);
write (LINESEPARATOR);
}
}
}

View file

@ -1,149 +0,0 @@
// ESAppNode.java
// Copyright (c) Hannes Wallnöfer 1998-2000
package helma.scripting.fesi;
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
* space as well as access to some app related runtime information.
*/
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();
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("setMaxThreads", new AppSetNumberOfEvaluators ("setMaxThreads", evaluator, fp));
putHiddenProperty("clearCache", new AppClearCache ("clearCache", evaluator, fp));
}
/**
* Overrides getProperty to return some app-specific properties
*/
public ESValue getProperty (String propname, int hash) throws EcmaScriptException {
if ("requestCount".equals (propname)) {
return new ESNumber (app.getRequestCount ());
}
if ("xmlrpcCount".equals (propname)) {
return new ESNumber (app.getXmlrpcCount ());
}
if ("errorCount".equals (propname)) {
return new ESNumber (app.getErrorCount ());
}
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);
}
return super.getProperty (propname, hash);
}
class AppCountEvaluators extends BuiltinFunctionObject {
AppCountEvaluators (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 0);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
return new ESNumber (app.countEvaluators ());
}
}
class AppCountFreeEvaluators extends BuiltinFunctionObject {
AppCountFreeEvaluators (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 0);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
return new ESNumber (app.countFreeEvaluators ());
}
}
class AppCountActiveEvaluators extends BuiltinFunctionObject {
AppCountActiveEvaluators (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 ());
}
}
class AppCountMaxActiveEvaluators extends BuiltinFunctionObject {
AppCountMaxActiveEvaluators (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 ());
}
}
class AppCountThreads extends BuiltinFunctionObject {
AppCountThreads (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 0);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
return new ESNumber (app.countThreads ());
}
}
class AppSetNumberOfEvaluators extends BuiltinFunctionObject {
AppSetNumberOfEvaluators (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
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 ());
}
}

View file

@ -0,0 +1,59 @@
package helma.scripting.fesi;
import java.util.Map;
import helma.objectmodel.INode;
import FESI.Interpreter.Evaluator;
import FESI.Exceptions.EcmaScriptException;
import FESI.Data.ESNull;
import FESI.Data.ESValue;
import FESI.Data.ESWrapper;
/**
* Wrap a Java Bean for use in EcmaScript.
*/
public class ESBeanWrapper extends ESWrapper {
FesiEvaluator eval;
public ESBeanWrapper (Object object, FesiEvaluator eval) {
super (object, eval.getEvaluator(),true);
this.eval = eval;
}
/**
* Wrap getProperty, return ESNode if INode would be returned.
*/
public ESValue getProperty(String propertyName, int hash)
throws EcmaScriptException {
try {
ESValue val = super.getProperty (propertyName, hash);
if (val instanceof ESWrapper && ((ESWrapper)val).getJavaObject() instanceof INode) {
return eval.getNodeWrapper( (INode) ((ESWrapper)val).getJavaObject() );
} else if (val instanceof ESWrapper && ((ESWrapper)val).getJavaObject() instanceof Map) {
return new ESMapWrapper(eval, (Map) ((ESWrapper)val).getJavaObject() );
} else {
return val;
}
} catch (Exception rte) {
return ESNull.theNull;
}
}
public void putProperty(String propertyName, ESValue propertyValue, int hash)
throws EcmaScriptException {
try {
super.putProperty (propertyName, propertyValue, hash);
} catch (Exception rte) {
// create a nice error message
throw new EcmaScriptException("can't set property " + propertyName +
" to this value on " + getJavaObject().toString() );
}
}
}

View file

@ -64,6 +64,8 @@ public class ESMapWrapper extends ESWrapper {
return new ESString ((String) val);
else if (val instanceof INode)
return fesi.getNodeWrapper ((INode) val);
else if (val instanceof Map)
return new ESMapWrapper (fesi, (Map)val);
else if (val instanceof ESValue)
return (ESValue) val;
return ESLoader.normalizeValue(val, evaluator);

View file

@ -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. <p>
* 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 ());
}
}

View file

@ -31,7 +31,7 @@ public class FesiActionAdapter {
Application app;
String sourceName;
// this is the parsed function which can be easily applied to RequestEvaluator objects
TypeUpdater pfunc;
TypeUpdater pfunc, pfuncAsString;
public FesiActionAdapter (ActionFile action) {
prototype = action.getPrototype ();
@ -40,45 +40,34 @@ public class FesiActionAdapter {
String functionName = action.getFunctionName ();
sourceName = action.toString ();
try {
pfunc = parseFunction (functionName, "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", content);
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);
}
// check if this is a template and we need to generate an "_as_string" variant
if (action instanceof Template) {
try {
pfuncAsString = parseFunction (functionName+"_as_string",
"arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10",
"res.pushStringBuffer(); "+content+"\r\nreturn res.popStringBuffer();\r\n");
} catch (Throwable x) {
String message = x.getMessage ();
pfunc = new ErrorFeedback (functionName+"_as_string", message);
}
} else {
pfuncAsString = null;
}
}
/* 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) {}
}
} */
/* 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());
}
} catch (Exception ignore) {}
}
} */
public synchronized void updateEvaluator (FesiEvaluator fesi) throws EcmaScriptException {
if (pfunc != null)
pfunc.updateEvaluator (fesi);
if (pfunc != null)
pfunc.updateEvaluator (fesi);
if (pfuncAsString != null)
pfuncAsString.updateEvaluator (fesi);
}
protected TypeUpdater parseFunction (String funcname, String params, String body) throws EcmaScriptException {

View file

@ -22,14 +22,14 @@ import Acme.LruHashtable;
* This is the implementation of ScriptingEnvironment for the FESI EcmaScript interpreter.
*/
public class FesiEvaluator {
public final class FesiEvaluator {
// the application we're running in
public Application app;
// The FESI evaluator
Evaluator evaluator;
// the global object
GlobalObject global;
@ -50,8 +50,13 @@ public class FesiEvaluator {
"helma.scripting.fesi.extensions.ImageExtension",
"helma.scripting.fesi.extensions.FtpExtension",
"FESI.Extensions.JavaAccess",
"helma.scripting.fesi.extensions.DomExtension",
"FESI.Extensions.OptionalRegExp"};
// remember global variables from last invokation to be able to
// do lazy cleanup
Map lastGlobals = null;
public FesiEvaluator (Application app, RequestEvaluator reval) {
this.app = app;
this.reval = reval;
@ -73,7 +78,7 @@ public class FesiEvaluator {
// fake a cache member like the one found in ESNodes
global.putHiddenProperty ("cache", new ESNode (new TransientNode ("cache"), this));
global.putHiddenProperty ("undefined", ESUndefined.theUndefined);
ESAppNode appnode = new ESAppNode (app.getAppNode (), this);
ESBeanWrapper appnode = new ESBeanWrapper (new ApplicationBean (app), this);
global.putHiddenProperty ("app", appnode);
initialize();
} catch (Exception e) {
@ -145,12 +150,13 @@ public class FesiEvaluator {
else
evaluateString (prototype, ff.getContent ());
}
/* for (Iterator it = prototype.templates.values().iterator(); it.hasNext(); ) {
for (Iterator it = prototype.templates.values().iterator(); it.hasNext(); ) {
Template tmp = (Template) it.next ();
try {
tmp.updateRequestEvaluator (reval);
FesiActionAdapter adp = new FesiActionAdapter (tmp);
adp.updateEvaluator (this);
} catch (EcmaScriptException ignore) {}
} */
}
for (Iterator it = prototype.actions.values().iterator(); it.hasNext(); ) {
ActionFile act = (ActionFile) it.next ();
try {
@ -204,7 +210,7 @@ public class FesiEvaluator {
esv[i] = ESLoader.normalizeValue (args[i], evaluator);
}
if (globals != null) {
if (globals != null && globals != lastGlobals) {
// remember all global variables before invocation
Set tmpGlobal = new HashSet ();
for (Enumeration en = global.getAllProperties(); en.hasMoreElements(); ) {
@ -221,13 +227,10 @@ public class FesiEvaluator {
// comfortable to EcmaScript coders, i.e. we use a lot of custom wrappers
// that expose properties and functions in a special way instead of just going
// with the standard java object wrappers.
if (v instanceof RequestTrans)
((RequestTrans) v).data = new ESMapWrapper (this, ((RequestTrans) v).getRequestData ());
else if (v instanceof ResponseTrans)
((ResponseTrans) v).data = new ESMapWrapper (this, ((ResponseTrans) v).getResponseData ());
if (v instanceof Map)
if (v instanceof Map) {
sv = new ESMapWrapper (this, (Map) v);
else if ("path".equals (k)) {
} else if ("path".equals (k)) {
ArrayPrototype parr = new ArrayPrototype (evaluator.getArrayPrototype(), evaluator);
List path = (List) v;
// register path elements with their prototype
@ -240,15 +243,21 @@ public class FesiEvaluator {
parr.putHiddenProperty (protoname, wrappedElement);
}
sv = parr;
} else if ("user".equals (k)) {
sv = getNodeWrapper ((User) v);
} else if ("req".equals (k)) {
sv = new ESBeanWrapper (new RequestBean ((RequestTrans) v), this);
} else if ("res".equals (k)) {
sv = new ESBeanWrapper (new ResponseBean ((ResponseTrans) v), this);
} else if ("session".equals (k)) {
sv = new ESBeanWrapper (new SessionBean ((Session)v), this);
} else if ("app".equals (k)) {
sv = new ESAppNode ((INode) v, this);
}
else
sv = new ESBeanWrapper (new ApplicationBean ((Application)v), this);
} else {
sv = ESLoader.normalizeValue (v, evaluator);
}
global.putHiddenProperty (k, sv);
}
// remember the globals set on this evaluator
// lastGlobals = globals;
}
evaluator.thread = Thread.currentThread ();
ESValue retval = eso.doIndirectCall (evaluator, eso, functionName, esv);
@ -262,8 +271,10 @@ public class FesiEvaluator {
String msg = x.getMessage ();
if (msg == null || msg.length() < 10)
msg = x.toString ();
System.err.println ("INVOKE-ERROR: "+msg);
x.printStackTrace ();
if (app.debug ()) {
System.err.println ("Error in Script: "+msg);
x.printStackTrace ();
}
throw new ScriptingException (msg);
} finally {
// remove global variables that have been added during invocation.
@ -274,8 +285,10 @@ public class FesiEvaluator {
if (globalVariables != null) {
for (Enumeration en = global.getAllProperties(); en.hasMoreElements(); ) {
String g = en.nextElement ().toString ();
if (!globalVariables.contains (g)) try {
global.deleteProperty (g, g.hashCode());
try {
if (!globalVariables.contains (g) &&
!(global.getProperty (g, g.hashCode()) instanceof BuiltinFunctionObject))
global.deleteProperty (g, g.hashCode());
} catch (Exception x) {
System.err.println ("Error resetting global property: "+g);
}
@ -420,7 +433,7 @@ public class FesiEvaluator {
* Get a script wrapper for an implemntation of helma.objectmodel.INode
*/
public ESNode getNodeWrapper (INode n) {
// FIXME: should this return ESNull.theNull?
if (n == null)
return null;
@ -443,12 +456,7 @@ public class FesiEvaluator {
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);
esn = new ESNode (op, evaluator, n, this);
wrappercache.put (n, esn);
// app.logEvent ("Wrapper for "+n+" created");
@ -466,28 +474,6 @@ public class FesiEvaluator {
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.
*/
@ -527,39 +513,13 @@ public class FesiEvaluator {
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 {
@ -568,18 +528,6 @@ public class FesiEvaluator {
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);
} */
}
}

View file

@ -14,7 +14,7 @@ import FESI.Exceptions.*;
/**
* This is the implementation of ScriptingEnvironment for the FESI EcmaScript interpreter.
*/
public class FesiScriptingEnvironment implements ScriptingEnvironment {
public final class FesiScriptingEnvironment implements ScriptingEnvironment {
Application app;
Properties props;
@ -74,7 +74,7 @@ public class FesiScriptingEnvironment implements ScriptingEnvironment {
return evaluators.values();
}
FesiEvaluator getEvaluator (RequestEvaluator reval) {
private FesiEvaluator getEvaluator (RequestEvaluator reval) {
FesiEvaluator fesi = (FesiEvaluator) evaluators.get (reval);
if (fesi == null) {
fesi = new FesiEvaluator (app, reval);

View file

@ -20,7 +20,7 @@ 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.
* (Node objects), the global prototype, the session object etc.
*/
public class HopExtension {
@ -61,8 +61,8 @@ public class HopExtension {
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 Session prototype
ObjectPrototype esSessionPrototype = new ObjectPrototype (esNodePrototype, evaluator);
// the Node constructor
ESObject node = new NodeConstructor ("Node", fp, fesi);
@ -97,12 +97,6 @@ public class HopExtension {
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("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));
@ -120,20 +114,11 @@ public class HopExtension {
go.putHiddenProperty("authenticate", new GlobalAuthenticate ("authenticate", evaluator, fp));
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));
// register object prototypes with FesiEvaluator
fesi.putPrototype ("global", go);
fesi.putPrototype ("hopobject", esNodePrototype);
fesi.putPrototype ("__javaobject__", esObjectPrototype);
fesi.putPrototype ("user", esUserPrototype);
// fesi.putPrototype ("session", esSessionPrototype);
}
class NodeAdd extends BuiltinFunctionObject {
@ -415,92 +400,6 @@ public class HopExtension {
}
}
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)
return ESBoolean.makeBoolean(false);
ESUser u = (ESUser) thisObject;
if (u.user == null)
throw new EcmaScriptException ("login() can only be called for user objects that are online at the moment!");
boolean success = app.loginUser (arguments[0].toString (), arguments[1].toString (), u);
try {
u.doIndirectCall (this.evaluator, u, "onLogin", new ESValue[0]);
} catch (Exception nosuch) {}
return ESBoolean.makeBoolean (success);
}
}
class UserRegister extends BuiltinFunctionObject {
UserRegister (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 2);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
if (arguments.length < 2)
return ESBoolean.makeBoolean(false);
INode unode = app.registerUser (arguments[0].toString (), arguments[1].toString ());
if (unode == null)
return ESNull.theNull;
else
return fesi.getNodeWrapper (unode);
}
}
class UserLogout extends BuiltinFunctionObject {
UserLogout (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
ESUser u = (ESUser) thisObject;
if (u.user == null)
return ESBoolean.makeBoolean (true);
try {
u.doIndirectCall (this.evaluator, u, "onLogout", new ESValue[0]);
} catch (Exception nosuch) {}
return ESBoolean.makeBoolean (app.logoutUser (u));
}
}
class UserOnSince extends BuiltinFunctionObject {
UserOnSince (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
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 ()));
return date;
}
}
class UserLastActive extends BuiltinFunctionObject {
UserLastActive (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
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 ()));
return date;
}
}
class UserTouch extends BuiltinFunctionObject {
UserTouch (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
ESUser u = (ESUser) thisObject;
if (u.user != null)
u.user.touch ();
return ESNull.theNull;
}
}
class GlobalGetProperty extends BuiltinFunctionObject {
GlobalGetProperty (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
@ -625,109 +524,6 @@ public class HopExtension {
}
class GlobalGetUser extends BuiltinFunctionObject {
GlobalGetUser (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
INode user = null;
if (arguments.length > 0) {
String uname = arguments[0].toString ().trim ();
user = app.getUserNode (uname);
}
if (user == null)
return ESNull.theNull;
return fesi.getNodeWrapper (user);
}
}
class GlobalGetUserBySession extends BuiltinFunctionObject {
GlobalGetUserBySession (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
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 ());
}
}
class GlobalGetAllUsers extends BuiltinFunctionObject {
GlobalGetAllUsers (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
INode users = app.getUserRoot ();
ESObject ap = this.evaluator.getArrayPrototype();
ArrayPrototype theArray = new ArrayPrototype(ap, this.evaluator);
int i=0;
for (Enumeration e=users.properties (); e.hasMoreElements (); ) {
String propname = (String) e.nextElement ();
theArray.putProperty (i++, new ESString (propname));
}
return theArray;
}
}
class GlobalGetActiveUsers extends BuiltinFunctionObject {
GlobalGetActiveUsers (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
Hashtable sessions = (Hashtable) app.sessions.clone ();
ESObject ap = this.evaluator.getArrayPrototype();
ArrayPrototype theArray = new ArrayPrototype (ap, this.evaluator);
theArray.setSize (sessions.size ());
int i=0;
// 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);
// }
}
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);
}
public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException {
if (arguments.length < 1)
return ESBoolean.makeBoolean (false);
String username = null;
boolean active = false;
if (arguments[0] instanceof ESUser) {
ESUser esu = (ESUser) arguments[0];
active = (esu.user != null);
} else {
active = app.activeUsers.contains (arguments[0].toString ());
}
return ESBoolean.makeBoolean (active);
}
}
class GlobalGetAge extends BuiltinFunctionObject {
GlobalGetAge (String name, Evaluator evaluator, FunctionPrototype fp) {
super (fp, evaluator, name, 1);

View file

@ -52,7 +52,7 @@ public class DomExtension extends Extension {
}
}
class XmlSave extends BuiltinFunctionObject {
class XmlSave extends BuiltinFunctionObject {
XmlSave(String name, Evaluator evaluator, FunctionPrototype fp) {
super(fp, evaluator, name, 1);
}
@ -69,6 +69,7 @@ public class DomExtension extends Extension {
try {
File tmpFile = new File(arguments[0].toString()+".tmp."+XmlWriter.generateID());
XmlWriter writer = new XmlWriter (tmpFile);
writer.setDatabaseMode(false);
boolean result = writer.write(node);
writer.close();
File finalFile = new File(arguments[0].toString());
@ -81,6 +82,10 @@ public class DomExtension extends Extension {
}
}
/**
* Xml.create() is used to get a string containing the xml-content.
* Useful if Xml-content should be made public through the web.
*/
class XmlCreate extends BuiltinFunctionObject {
XmlCreate(String name, Evaluator evaluator, FunctionPrototype fp) {
super(fp, evaluator, name, 1);
@ -98,6 +103,7 @@ public class DomExtension extends Extension {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
XmlWriter writer = new XmlWriter (out);
writer.setDatabaseMode(false);
boolean result = writer.write(node);
writer.flush();
return new ESString (out.toString());
@ -123,7 +129,7 @@ public class DomExtension extends Extension {
}
try {
XmlReader reader = new XmlReader ();
INode result = reader.read (arguments[0].toString(),node);
INode result = reader.read (new File(arguments[0].toString()),node);
return this.evaluator.reval.getNodeWrapper (result);
} catch ( NoClassDefFoundError e ) {
throw new EcmaScriptException ("Can't load dom-capable xml parser.");

View file

@ -9,7 +9,6 @@ import javax.mail.internet.*;
import javax.activation.*;
import java.io.*;
import java.util.*;
import helma.framework.core.*;
import helma.util.*;
import FESI.Data.*;
import FESI.Interpreter.*;

View file

@ -21,13 +21,22 @@ import helma.util.*;
*/
public abstract class AbstractServletClient extends HttpServlet {
// host on which Helma app is running
String host = null;
// port of Helma RMI server
int port = 0;
int uploadLimit; // limit to HTTP uploads in kB
// limit to HTTP uploads in kB
int uploadLimit;
// RMI url of Helma app
String hopUrl;
// cookie domain to use
String cookieDomain;
// default encoding for requests
String defaultEncoding;
// allow caching of responses
boolean caching;
// enable debug output
boolean debug;
static final byte HTTP_GET = 0;
@ -35,7 +44,7 @@ public abstract class AbstractServletClient extends HttpServlet {
public void init (ServletConfig init) throws ServletException {
super.init (init);
host = init.getInitParameter ("host");
if (host == null) host = "localhost";
@ -49,6 +58,8 @@ public abstract class AbstractServletClient extends HttpServlet {
hopUrl = "//" + host + ":" + port + "/";
defaultEncoding = init.getInitParameter ("charset");
debug = ("true".equalsIgnoreCase (init.getInitParameter ("debug")));
caching = ! ("false".equalsIgnoreCase (init.getInitParameter ("caching")));
@ -88,13 +99,15 @@ public abstract class AbstractServletClient extends HttpServlet {
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
Map parameters = parseParameters (request);
for (Iterator i=parameters.entrySet().iterator(); i.hasNext(); ) {
Map.Entry entry = (Map.Entry) i.next ();
String key = (String) entry.getKey ();
String[] values = (String[]) entry.getValue ();
if (values != null && values.length > 0) {
reqtrans.set (key, values[0]); // set to single string value
if (values.length > 1)
reqtrans.set (key+"_array", values); // set string array
}
}
@ -102,13 +115,13 @@ public abstract class AbstractServletClient extends HttpServlet {
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(); ) {
FileUpload upload = getUpload (request);
if (upload != null) {
Hashtable parts = upload.getParts ();
for (Enumeration e = parts.keys(); e.hasMoreElements(); ) {
String nextKey = (String) e.nextElement ();
Object nextPart = upload.get (nextKey);
Object nextPart = parts.get (nextKey);
reqtrans.set (nextKey, nextPart);
}
}
@ -226,6 +239,13 @@ public abstract class AbstractServletClient extends HttpServlet {
res.setHeader( "WWW-Authenticate", "Basic realm=\"" + trans.realm + "\"" );
if (trans.status > 0)
res.setStatus (trans.status);
// if we don't know which charset to use for parsing HTTP params,
// take the one from the response. This usually works because
// browsers send parrameters in the same encoding as the page
// containing the form has. Problem is we can do this only per servlet,
// not per session or even per page, which would produce too much overhead
if (defaultEncoding == null)
defaultEncoding = trans.charset;
res.setContentLength (trans.getContentLength ());
res.setContentType (trans.getContentType ());
try {
@ -245,10 +265,10 @@ public abstract class AbstractServletClient extends HttpServlet {
}
public Uploader getUpload (HttpServletRequest request) throws Exception {
public FileUpload getUpload (HttpServletRequest request) throws Exception {
int contentLength = request.getContentLength ();
BufferedInputStream in = new BufferedInputStream (request.getInputStream ());
Uploader up = null;
FileUpload upload = null;
try {
if (contentLength > uploadLimit*1024) {
// consume all input to make Apache happy
@ -259,42 +279,165 @@ public abstract class AbstractServletClient extends HttpServlet {
throw new RuntimeException ("Upload exceeds limit of "+uploadLimit+" kb.");
}
String contentType = request.getContentType ();
up = new Uploader(uploadLimit);
up.load (in, contentType, contentLength);
upload = new FileUpload(uploadLimit);
upload.load (in, contentType, contentLength);
} finally {
try { in.close (); } catch (Exception ignore) {}
try {
in.close ();
} catch (Exception ignore) {}
}
return up;
return upload;
}
public Object getUploadPart(Uploader up, String name) {
return up.getParts().get(name);
public Object getUploadPart(FileUpload upload, String name) {
return upload.getParts().get(name);
}
/**
* Put name value pair in map.
*
* @param b the character value byte
*
* Put name and value pair in map. When name already exist, add value
* to array of values.
*/
private static void putMapEntry( Map map, String name, String value) {
String[] newValues = null;
String[] oldValues = (String[]) map.get(name);
if (oldValues == null) {
newValues = new String[1];
newValues[0] = value;
} else {
newValues = new String[oldValues.length + 1];
System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
newValues[oldValues.length] = value;
}
map.put(name, newValues);
}
protected Map parseParameters (HttpServletRequest request) {
String encoding = request.getCharacterEncoding ();
if (encoding == null)
// no encoding from request, use standard one
encoding = defaultEncoding;
if (encoding == null)
encoding = "ISO-8859-1";
HashMap parameters = new HashMap ();
// Parse any query string parameters from the request
try {
parseParameters (parameters, request.getQueryString().getBytes(), encoding);
} catch (Exception e) {
}
// Parse any posted parameters in the input stream
if ("POST".equals(request.getMethod()) &&
"application/x-www-form-urlencoded".equals(request.getContentType())) {
try {
int max = request.getContentLength();
int len = 0;
byte buf[] = new byte[max];
ServletInputStream is = request.getInputStream();
while (len < max) {
int next = is.read(buf, len, max - len);
if (next < 0 ) {
break;
}
len += next;
}
// is.close();
parseParameters(parameters, buf, encoding);
} catch (IllegalArgumentException e) {
} catch (IOException e) {
}
}
return parameters;
}
/**
* Append request parameters from the specified String to the specified
* Map. It is presumed that the specified Map is not accessed from any
* other thread, so no synchronization is performed.
* <p>
* <strong>IMPLEMENTATION NOTE</strong>: URL decoding is performed
* individually on the parsed name and value elements, rather than on
* the entire query string ahead of time, to properly deal with the case
* where the name or value includes an encoded "=" or "&" character
* that would otherwise be interpreted as a delimiter.
*
* NOTE: byte array data is modified by this method. Caller beware.
*
* @param map Map that accumulates the resulting parameters
* @param data Input string containing request parameters
* @param encoding Encoding to use for converting hex
*
* @exception UnsupportedEncodingException if the data is malformed
*/
public static void parseParameters (Map map, byte[] data, String encoding)
throws UnsupportedEncodingException {
if (data != null && data.length > 0) {
int pos = 0;
int ix = 0;
int ox = 0;
String key = null;
String value = null;
while (ix < data.length) {
byte c = data[ix++];
switch ((char) c) {
case '&':
value = new String(data, 0, ox, encoding);
if (key != null) {
putMapEntry(map, key, value);
key = null;
}
ox = 0;
break;
case '=':
key = new String(data, 0, ox, encoding);
ox = 0;
break;
case '+':
data[ox++] = (byte)' ';
break;
case '%':
data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4)
+ convertHexDigit(data[ix++]));
break;
default:
data[ox++] = c;
}
}
//The last value does not end in '&'. So save it now.
if (key != null) {
value = new String(data, 0, ox, encoding);
putMapEntry(map, key, value);
}
}
}
/**
* Convert a byte character value to hexidecimal digit value.
*
* @param b the character value byte
*/
private static byte convertHexDigit( byte b ) {
if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
return 0;
}
public String getServletInfo(){
return new String("Hop Servlet Client");
return new String("Helma Servlet Client");
}
}

View file

@ -1,271 +0,0 @@
// AcmeServletClient.java
// Copyright (c) Hannes Wallnoefer, 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.util.*;
import helma.framework.*;
import helma.framework.core.Application;
import helma.util.Uploader;
/**
* 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 {
private int uploadLimit; // limit to HTTP uploads in kB
private Hashtable apps;
private Application app;
private String cookieDomain;
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
}
public void init (ServletConfig config) throws ServletException {
super.init (config);
// do nothing
}
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);
}
private void execute (HttpServletRequest request, HttpServletResponse response, byte method) {
String protocol = request.getProtocol ();
Cookie[] cookies = request.getCookies();
try {
RequestTrans reqtrans = new RequestTrans (method);
// 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
}
}
// 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 (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);
}
}
// 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);
} catch (Exception x) {
x.printStackTrace ();
try {
response.setContentType ("text/html");
Writer out = response.getWriter ();
if (debug)
out.write ("<b>Error:</b><br>" +x);
else
out.write ("This server is temporarily unavailable. Please check back later.");
out.flush ();
} catch (Exception io_e) {}
}
}
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.getRedirect () != null) {
try {
res.sendRedirect(trans.getRedirect ());
} catch(Exception io_e) {}
} else {
if (!trans.cache) {
// 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) { System.out.println ("Error in writeResponse: "+io_e); }
}
}
private void redirectResponse (HttpServletRequest request, HttpServletResponse res, ResponseTrans trans, String url) {
try {
res.sendRedirect(url);
} catch (Exception e) {
System.err.println ("Exception in redirect: " + e + e.getMessage());
}
}
public Uploader getUpload (HttpServletRequest request) throws Exception {
return getUpload (500, request);
}
public Uploader getUpload (int maxKbytes, HttpServletRequest request) throws Exception {
int contentLength = request.getContentLength ();
BufferedInputStream in = new BufferedInputStream (request.getInputStream ());
Uploader up = null;
if (contentLength > maxKbytes*1024) {
throw new RuntimeException ("Upload exceeds limit of "+maxKbytes+" kb.");
}
String contentType = request.getContentType ();
up = new Uploader(maxKbytes);
up.load (in, contentType, contentLength);
return up;
}
public Object getUploadPart(Uploader up, String name) {
return up.getParts().get(name);
}
public String getServletInfo (){
return new String("Hop 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] == '/')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == '/')) {
len--;
}
return ((st > 0) || (len < val.length)) ? new String (val, st, len-st) : str;
}
}

View file

@ -0,0 +1,78 @@
// EmbeddedServletClient.java
// Copyright (c) Hannes Wallnöfer, 2002
package helma.servlet;
import javax.servlet.*;
import java.io.*;
import java.util.*;
import helma.framework.*;
import helma.framework.core.Application;
import helma.main.*;
import helma.util.*;
/**
* Servlet client that runs a Helma application for the embedded
* web server
*/
public final class EmbeddedServletClient extends AbstractServletClient {
private Application app = null;
private String appName;
// tells us whether the application is mounted as root or by its name
// depending on this we know whether we have to transform the request path
boolean root;
public EmbeddedServletClient (String appName, boolean isRoot) {
this.appName = appName;
this.root = isRoot;
}
IRemoteApp getApp (String appID) {
if (app == null)
app = Server.getServer().getApplication (appName);
return app;
}
void invalidateApp (String appID) {
// do nothing
}
String getAppID (String path) {
return appName;
}
String getRequestPath (String path) {
if (path == null)
return "";
if (root)
return trim (path);
int appInPath = path.indexOf (appName);
if (appInPath > 0)
return trim (path.substring (appInPath+appName.length()));
else
return trim (path);
}
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;
}
}

View file

@ -70,17 +70,17 @@ public class MultiServletClient extends AbstractServletClient {
int len = val.length;
int st = 0;
// advance to start of path
// advance to start of path, eating up any slashes
while ((st < len) && (val[st] <= ' ' || val[st] == '/'))
st++;
// eat characters of first path element
// advance until slash ending the first path element
while (st < len && val[st] != '/')
st++;
if (st < len && val[st] == '/')
st++;
// eat away noise at end of path
// eat away spaces and slashes at end of path
while ((st < len) && (val[len - 1] <= ' ' || val[len - 1] == '/'))
len--;

View file

@ -1,44 +1,36 @@
// ServletClient.java
// Copyright (c) Hannes Wallnöfer, Raphael Spannocchi 1998-2000
// Copyright (c) Hannes Wallnöfer, Raphael Spannocchi 1998-2002
/* 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 the HOP servlet adapter. This class communicates with just
* one Hop application.
* This is the standard Helma servlet adapter. This class represents a servlet
* that is dedicated to one Helma application over RMI.
*/
public class ServletClient extends AbstractServletClient {
private IRemoteApp app = null;
private String appName;
private String appName = null;
public void init (ServletConfig init) throws ServletException {
super.init (init);
appName = init.getInitParameter ("application");
if (appName == null)
appName = "base";
super.init (init);
}
IRemoteApp getApp (String appID) throws Exception {
if (app != null)
return app;
if (appName == null)
throw new ServletException ("Helma application name not specified for helma.servlet.ServletClient");
app = (IRemoteApp) Naming.lookup (hopUrl + appName);
return app;
}
@ -76,7 +68,7 @@ public class ServletClient extends AbstractServletClient {
// for testing
public static void main (String args[]) {
AbstractServletClient client = new ServletClient ();
String path = "///appname/do/it/for/me///";
String path = "///appname/some/random/path///";
System.out.println (client.getAppID (path));
System.out.println (client.getRequestPath (path));
}
@ -85,20 +77,3 @@ public class ServletClient extends AbstractServletClient {
}

View file

@ -4,16 +4,15 @@
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.
* Standalone servlet client that runs a Helma application all by itself
* in embedded mode without relying on helma.main.Server.
*/
public final class StandaloneServletClient extends AbstractServletClient {
@ -22,7 +21,7 @@ public final class StandaloneServletClient extends AbstractServletClient {
private String appName;
private String serverProps;
public void init (ServletConfig init) throws ServletException {
super.init (init);
appName = init.getInitParameter ("application");
@ -105,13 +104,11 @@ public final class StandaloneServletClient extends AbstractServletClient {
// for testing
public static void main (String args[]) {
AbstractServletClient client = new ServletClient ();
String path = "///appname/do/it/for/me///";
String path = "///appname/some/random/path///";
System.out.println (client.getAppID (path));
System.out.println (client.getRequestPath (path));
}
}

View file

@ -1,4 +1,4 @@
// Uploader.java
// FileUpload.java
// Copyright (c) Hannes Wallnöfer 1996-2000
package helma.util;
@ -8,19 +8,19 @@ import java.io.*;
import java.util.*;
/**
* Utility class for file uploads via HTTP POST.
* Utility class for MIME file uploads via HTTP POST.
*/
public class Uploader {
public class FileUpload {
public Hashtable parts;
int maxKbytes;
public Uploader () {
maxKbytes = 500;
public FileUpload () {
maxKbytes = 1024;
}
public Uploader (int max) {
public FileUpload (int max) {
maxKbytes = max;
}

View file

@ -17,109 +17,146 @@ import java.text.*;
public final class HtmlEncoder {
// transformation table for characters 128 to 255. These actually fall into two
// groups, put together for efficiency: "Windows" chacacters 128-159 such as
// "smart quotes", which are encoded to valid Unicode entities, and
// valid ISO-8859 caracters 160-255, which are encoded to the symbolic HTML
// entity. Everything >= 256 is encoded to a numeric entity.
//
// for mor on HTML entities see http://www.pemberley.com/janeinfo/latin1.html and
// ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT
//
static final String[] transform = {
"&euro;", // 128
"", // empty string means character is undefined in unicode
"&#8218;",
"&#402;",
"&#8222;",
"&#8230;",
"&#8224;",
"&#8225;",
"&#710;",
"&#8240;",
"&#352;",
"&#8249;",
"&#338;",
"",
"&#381;",
"",
"",
"&#8216;",
"&#8217;",
"&#8220;",
"&#8221;",
"&#8226;",
"&#8211;",
"&#8212;",
"&#732;",
"&#8482;",
"&#353;",
"&#8250;",
"&#339;",
"",
"&#382;",
"&#376;", // 159
"&nbsp;", // 160
"&iexcl;",
"&cent;",
"&pound;",
"&curren;",
"&yen;",
"&brvbar;",
"&sect;",
"&uml;",
"&copy;",
"&ordf;",
"&laquo;",
"&not;",
"&shy;",
"&reg;",
"&macr;",
"&deg;",
"&plusmn;",
"&sup2;",
"&sup3;",
"&acute;",
"&micro;",
"&para;",
"&middot;",
"&cedil;",
"&sup1;",
"&ordm;",
"&raquo;",
"&frac14;",
"&frac12;",
"&frac34;",
"&iquest;",
"&Agrave;",
"&Aacute;",
"&Acirc;",
"&Atilde;",
"&Auml;",
"&Aring;",
"&AElig;",
"&Ccedil;",
"&Egrave;",
"&Eacute;",
"&Ecirc;",
"&Euml;",
"&Igrave;",
"&Iacute;",
"&Icirc;",
"&Iuml;",
"&ETH;",
"&Ntilde;",
"&Ograve;",
"&Oacute;",
"&Ocirc;",
"&Otilde;",
"&Ouml;",
"&times;",
"&Oslash;",
"&Ugrave;",
"&Uacute;",
"&Ucirc;",
"&Uuml;",
"&Yacute;",
"&THORN;",
"&szlig;",
"&agrave;",
"&aacute;",
"&acirc;",
"&atilde;",
"&auml;",
"&aring;",
"&aelig;",
"&ccedil;",
"&egrave;",
"&eacute;",
"&ecirc;",
"&euml;",
"&igrave;",
"&iacute;",
"&icirc;",
"&iuml;",
"&eth;",
"&ntilde;",
"&ograve;",
"&oacute;",
"&ocirc;",
"&otilde;",
"&ouml;",
"&divide;",
"&oslash;",
"&ugrave;",
"&uacute;",
"&ucirc;",
"&uuml;",
"&yacute;",
"&thorn;",
"&yuml;" // 255
};
/*
static final Hashtable convertor = new Hashtable (128);
// conversion table
static {
convertor.put(new Integer(160), "&nbsp;");
convertor.put(new Integer(161), "&iexcl;");
convertor.put(new Integer(162), "&cent;");
convertor.put(new Integer(163), "&pound;");
convertor.put(new Integer(164), "&curren;");
convertor.put(new Integer(165), "&yen;");
convertor.put(new Integer(166), "&brvbar;");
convertor.put(new Integer(167), "&sect;");
convertor.put(new Integer(168), "&uml;");
convertor.put(new Integer(169), "&copy;");
convertor.put(new Integer(170), "&ordf;");
convertor.put(new Integer(171), "&laquo;");
convertor.put(new Integer(172), "&not;");
convertor.put(new Integer(173), "&shy;");
convertor.put(new Integer(174), "&reg;");
convertor.put(new Integer(175), "&macr;");
convertor.put(new Integer(176), "&deg;");
convertor.put(new Integer(177), "&plusmn;");
convertor.put(new Integer(178), "&sup2;");
convertor.put(new Integer(179), "&sup3;");
convertor.put(new Integer(180), "&acute;");
convertor.put(new Integer(181), "&micro;");
convertor.put(new Integer(182), "&para;");
convertor.put(new Integer(183), "&middot;");
convertor.put(new Integer(184), "&cedil;");
convertor.put(new Integer(185), "&sup1;");
convertor.put(new Integer(186), "&ordm;");
convertor.put(new Integer(187), "&raquo;");
convertor.put(new Integer(188), "&frac14;");
convertor.put(new Integer(189), "&frac12;");
convertor.put(new Integer(190), "&frac34;");
convertor.put(new Integer(191), "&iquest;");
convertor.put(new Integer(192), "&Agrave;");
convertor.put(new Integer(193), "&Aacute;");
convertor.put(new Integer(194), "&Acirc;");
convertor.put(new Integer(195), "&Atilde;");
convertor.put(new Integer(196), "&Auml;");
convertor.put(new Integer(197), "&Aring;");
convertor.put(new Integer(198), "&AElig;");
convertor.put(new Integer(199), "&Ccedil;");
convertor.put(new Integer(200), "&Egrave;");
convertor.put(new Integer(201), "&Eacute;");
convertor.put(new Integer(202), "&Ecirc;");
convertor.put(new Integer(203), "&Euml;");
convertor.put(new Integer(204), "&Igrave;");
convertor.put(new Integer(205), "&Iacute;");
convertor.put(new Integer(206), "&Icirc;");
convertor.put(new Integer(207), "&Iuml;");
convertor.put(new Integer(208), "&ETH;");
convertor.put(new Integer(209), "&Ntilde;");
convertor.put(new Integer(210), "&Ograve;");
convertor.put(new Integer(211), "&Oacute;");
convertor.put(new Integer(212), "&Ocirc;");
convertor.put(new Integer(213), "&Otilde;");
convertor.put(new Integer(214), "&Ouml;");
convertor.put(new Integer(215), "&times;");
convertor.put(new Integer(216), "&Oslash;");
convertor.put(new Integer(217), "&Ugrave;");
convertor.put(new Integer(218), "&Uacute;");
convertor.put(new Integer(219), "&Ucirc;");
convertor.put(new Integer(220), "&Uuml;");
convertor.put(new Integer(221), "&Yacute;");
convertor.put(new Integer(222), "&THORN;");
convertor.put(new Integer(223), "&szlig;");
convertor.put(new Integer(224), "&agrave;");
convertor.put(new Integer(225), "&aacute;");
convertor.put(new Integer(226), "&acirc;");
convertor.put(new Integer(227), "&atilde;");
convertor.put(new Integer(228), "&auml;");
convertor.put(new Integer(229), "&aring;");
convertor.put(new Integer(230), "&aelig;");
convertor.put(new Integer(231), "&ccedil;");
convertor.put(new Integer(232), "&egrave;");
convertor.put(new Integer(233), "&eacute;");
convertor.put(new Integer(234), "&ecirc;");
convertor.put(new Integer(235), "&euml;");
convertor.put(new Integer(236), "&igrave;");
convertor.put(new Integer(237), "&iacute;");
convertor.put(new Integer(238), "&icirc;");
convertor.put(new Integer(239), "&iuml;");
convertor.put(new Integer(240), "&eth;");
convertor.put(new Integer(241), "&ntilde;");
convertor.put(new Integer(242), "&ograve;");
convertor.put(new Integer(243), "&oacute;");
convertor.put(new Integer(244), "&ocirc;");
convertor.put(new Integer(245), "&otilde;");
convertor.put(new Integer(246), "&ouml;");
convertor.put(new Integer(247), "&divide;");
convertor.put(new Integer(248), "&oslash;");
convertor.put(new Integer(249), "&ugrave;");
convertor.put(new Integer(250), "&uacute;");
convertor.put(new Integer(251), "&ucirc;");
convertor.put(new Integer(252), "&uuml;");
convertor.put(new Integer(253), "&yacute;");
convertor.put(new Integer(254), "&thorn;");
convertor.put(new Integer(255), "&yuml;");
} */
/**
*
@ -182,7 +219,7 @@ public final class HtmlEncoder {
case '\n':
ret.append ('\n');
if (!ignoreNewline && !swallowOneNewline)
ret.append ("<br>");
ret.append ("<br />");
if (!tagOpen)
swallowOneNewline = false;
break;
@ -197,16 +234,16 @@ 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 (";");
// }
// ret.append (c);
if (c < 128)
ret.append (c);
else if (c >= 128 && c < 256)
ret.append (transform[c-128]);
else {
ret.append ("&#");
ret.append ((int) c);
ret.append (";");
}
if (!tagOpen && !Character.isWhitespace (c))
swallowOneNewline = false;
}
@ -267,20 +304,20 @@ public final class HtmlEncoder {
case '\n':
ret.append ('\n');
if (encodeNewline) {
ret.append ("<br>");
ret.append ("<br />");
}
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 (";");
// }
// ret.append (c);
if (c < 128)
ret.append (c);
else if (c >= 128 && c < 256)
ret.append (transform[c-128]);
else {
ret.append ("&#");
ret.append ((int) c);
ret.append (";");
}
}
}
}
@ -315,5 +352,26 @@ public final class HtmlEncoder {
}
}
// test method
public static String printCharRange (int from, int to) {
StringBuffer response = new StringBuffer();
for (int i=from;i<to;i++) {
response.append (i);
response.append (" ");
response.append ((char) i);
response.append (" ");
if (i < 128)
response.append ((char) i);
else if (i >= 128 && i < 256)
response.append (transform[i-128]);
else {
response.append ("&#");
response.append (i);
response.append (";");
}
response.append ("\r\n");
}
return response.toString();
}
} // end of class

View file

@ -141,6 +141,12 @@ public final class SystemProperties extends Properties {
return props.keys();
}
public Set keySet () {
if (System.currentTimeMillis () - lastcheck > cacheTime)
checkFile ();
return props.keySet();
}
public Enumeration elements () {
if (System.currentTimeMillis () - lastcheck > cacheTime)
checkFile ();