This commit was manufactured by cvs2svn to create tag
'v_1_2_feature_complete'.
This commit is contained in:
parent
84f281fb77
commit
e1033484c6
51 changed files with 2080 additions and 2145 deletions
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
61
src/helma/framework/RequestBean.java
Normal file
61
src/helma/framework/RequestBean.java
Normal 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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
138
src/helma/framework/ResponseBean.java
Normal file
138
src/helma/framework/ResponseBean.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
181
src/helma/framework/core/ApplicationBean.java
Normal file
181
src/helma/framework/core/ApplicationBean.java
Normal 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() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
131
src/helma/framework/core/Session.java
Normal file
131
src/helma/framework/core/Session.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
73
src/helma/framework/core/SessionBean.java
Normal file
73
src/helma/framework/core/SessionBean.java
Normal 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 ());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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+" ");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import helma.framework.*;
|
|||
import helma.framework.core.*;
|
||||
import helma.xmlrpc.*;
|
||||
import helma.util.*;
|
||||
import com.sleepycat.db.*;
|
||||
|
||||
|
||||
/**
|
||||
|
|
49
src/helma/objectmodel/DatabaseException.java
Normal file
49
src/helma/objectmodel/DatabaseException.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
35
src/helma/objectmodel/IDatabase.java
Normal file
35
src/helma/objectmodel/IDatabase.java
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
18
src/helma/objectmodel/ITransaction.java
Normal file
18
src/helma/objectmodel/ITransaction.java
Normal 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 {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -58,7 +58,7 @@ public final class IDGenerator implements Serializable {
|
|||
/**
|
||||
* Get the current counter value
|
||||
*/
|
||||
protected long getValue () {
|
||||
public long getValue () {
|
||||
return counter;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
95
src/helma/objectmodel/db/XmlDatabase.java
Normal file
95
src/helma/objectmodel/db/XmlDatabase.java
Normal 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;
|
||||
}
|
||||
}
|
37
src/helma/objectmodel/dom/IDGenParser.java
Normal file
37
src/helma/objectmodel/dom/IDGenParser.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
||||
}
|
||||
|
|
|
@ -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 ) {
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ());
|
||||
}
|
||||
|
||||
}
|
||||
|
59
src/helma/scripting/fesi/ESBeanWrapper.java
Normal file
59
src/helma/scripting/fesi/ESBeanWrapper.java
Normal 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() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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 ());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
} */
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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.*;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
78
src/helma/servlet/EmbeddedServletClient.java
Normal file
78
src/helma/servlet/EmbeddedServletClient.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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--;
|
||||
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 = {
|
||||
"€", // 128
|
||||
"", // empty string means character is undefined in unicode
|
||||
"‚",
|
||||
"ƒ",
|
||||
"„",
|
||||
"…",
|
||||
"†",
|
||||
"‡",
|
||||
"ˆ",
|
||||
"‰",
|
||||
"Š",
|
||||
"‹",
|
||||
"Œ",
|
||||
"",
|
||||
"Ž",
|
||||
"",
|
||||
"",
|
||||
"‘",
|
||||
"’",
|
||||
"“",
|
||||
"”",
|
||||
"•",
|
||||
"–",
|
||||
"—",
|
||||
"˜",
|
||||
"™",
|
||||
"š",
|
||||
"›",
|
||||
"œ",
|
||||
"",
|
||||
"ž",
|
||||
"Ÿ", // 159
|
||||
" ", // 160
|
||||
"¡",
|
||||
"¢",
|
||||
"£",
|
||||
"¤",
|
||||
"¥",
|
||||
"¦",
|
||||
"§",
|
||||
"¨",
|
||||
"©",
|
||||
"ª",
|
||||
"«",
|
||||
"¬",
|
||||
"­",
|
||||
"®",
|
||||
"¯",
|
||||
"°",
|
||||
"±",
|
||||
"²",
|
||||
"³",
|
||||
"´",
|
||||
"µ",
|
||||
"¶",
|
||||
"·",
|
||||
"¸",
|
||||
"¹",
|
||||
"º",
|
||||
"»",
|
||||
"¼",
|
||||
"½",
|
||||
"¾",
|
||||
"¿",
|
||||
"À",
|
||||
"Á",
|
||||
"Â",
|
||||
"Ã",
|
||||
"Ä",
|
||||
"Å",
|
||||
"Æ",
|
||||
"Ç",
|
||||
"È",
|
||||
"É",
|
||||
"Ê",
|
||||
"Ë",
|
||||
"Ì",
|
||||
"Í",
|
||||
"Î",
|
||||
"Ï",
|
||||
"Ð",
|
||||
"Ñ",
|
||||
"Ò",
|
||||
"Ó",
|
||||
"Ô",
|
||||
"Õ",
|
||||
"Ö",
|
||||
"×",
|
||||
"Ø",
|
||||
"Ù",
|
||||
"Ú",
|
||||
"Û",
|
||||
"Ü",
|
||||
"Ý",
|
||||
"Þ",
|
||||
"ß",
|
||||
"à",
|
||||
"á",
|
||||
"â",
|
||||
"ã",
|
||||
"ä",
|
||||
"å",
|
||||
"æ",
|
||||
"ç",
|
||||
"è",
|
||||
"é",
|
||||
"ê",
|
||||
"ë",
|
||||
"ì",
|
||||
"í",
|
||||
"î",
|
||||
"ï",
|
||||
"ð",
|
||||
"ñ",
|
||||
"ò",
|
||||
"ó",
|
||||
"ô",
|
||||
"õ",
|
||||
"ö",
|
||||
"÷",
|
||||
"ø",
|
||||
"ù",
|
||||
"ú",
|
||||
"û",
|
||||
"ü",
|
||||
"ý",
|
||||
"þ",
|
||||
"ÿ" // 255
|
||||
};
|
||||
|
||||
/*
|
||||
static final Hashtable convertor = new Hashtable (128);
|
||||
|
||||
// conversion table
|
||||
static {
|
||||
convertor.put(new Integer(160), " ");
|
||||
convertor.put(new Integer(161), "¡");
|
||||
convertor.put(new Integer(162), "¢");
|
||||
convertor.put(new Integer(163), "£");
|
||||
convertor.put(new Integer(164), "¤");
|
||||
convertor.put(new Integer(165), "¥");
|
||||
convertor.put(new Integer(166), "¦");
|
||||
convertor.put(new Integer(167), "§");
|
||||
convertor.put(new Integer(168), "¨");
|
||||
convertor.put(new Integer(169), "©");
|
||||
convertor.put(new Integer(170), "ª");
|
||||
convertor.put(new Integer(171), "«");
|
||||
convertor.put(new Integer(172), "¬");
|
||||
convertor.put(new Integer(173), "­");
|
||||
convertor.put(new Integer(174), "®");
|
||||
convertor.put(new Integer(175), "¯");
|
||||
convertor.put(new Integer(176), "°");
|
||||
convertor.put(new Integer(177), "±");
|
||||
convertor.put(new Integer(178), "²");
|
||||
convertor.put(new Integer(179), "³");
|
||||
convertor.put(new Integer(180), "´");
|
||||
convertor.put(new Integer(181), "µ");
|
||||
convertor.put(new Integer(182), "¶");
|
||||
convertor.put(new Integer(183), "·");
|
||||
convertor.put(new Integer(184), "¸");
|
||||
convertor.put(new Integer(185), "¹");
|
||||
convertor.put(new Integer(186), "º");
|
||||
convertor.put(new Integer(187), "»");
|
||||
convertor.put(new Integer(188), "¼");
|
||||
convertor.put(new Integer(189), "½");
|
||||
convertor.put(new Integer(190), "¾");
|
||||
convertor.put(new Integer(191), "¿");
|
||||
convertor.put(new Integer(192), "À");
|
||||
convertor.put(new Integer(193), "Á");
|
||||
convertor.put(new Integer(194), "Â");
|
||||
convertor.put(new Integer(195), "Ã");
|
||||
convertor.put(new Integer(196), "Ä");
|
||||
convertor.put(new Integer(197), "Å");
|
||||
convertor.put(new Integer(198), "Æ");
|
||||
convertor.put(new Integer(199), "Ç");
|
||||
convertor.put(new Integer(200), "È");
|
||||
convertor.put(new Integer(201), "É");
|
||||
convertor.put(new Integer(202), "Ê");
|
||||
convertor.put(new Integer(203), "Ë");
|
||||
convertor.put(new Integer(204), "Ì");
|
||||
convertor.put(new Integer(205), "Í");
|
||||
convertor.put(new Integer(206), "Î");
|
||||
convertor.put(new Integer(207), "Ï");
|
||||
convertor.put(new Integer(208), "Ð");
|
||||
convertor.put(new Integer(209), "Ñ");
|
||||
convertor.put(new Integer(210), "Ò");
|
||||
convertor.put(new Integer(211), "Ó");
|
||||
convertor.put(new Integer(212), "Ô");
|
||||
convertor.put(new Integer(213), "Õ");
|
||||
convertor.put(new Integer(214), "Ö");
|
||||
convertor.put(new Integer(215), "×");
|
||||
convertor.put(new Integer(216), "Ø");
|
||||
convertor.put(new Integer(217), "Ù");
|
||||
convertor.put(new Integer(218), "Ú");
|
||||
convertor.put(new Integer(219), "Û");
|
||||
convertor.put(new Integer(220), "Ü");
|
||||
convertor.put(new Integer(221), "Ý");
|
||||
convertor.put(new Integer(222), "Þ");
|
||||
convertor.put(new Integer(223), "ß");
|
||||
convertor.put(new Integer(224), "à");
|
||||
convertor.put(new Integer(225), "á");
|
||||
convertor.put(new Integer(226), "â");
|
||||
convertor.put(new Integer(227), "ã");
|
||||
convertor.put(new Integer(228), "ä");
|
||||
convertor.put(new Integer(229), "å");
|
||||
convertor.put(new Integer(230), "æ");
|
||||
convertor.put(new Integer(231), "ç");
|
||||
convertor.put(new Integer(232), "è");
|
||||
convertor.put(new Integer(233), "é");
|
||||
convertor.put(new Integer(234), "ê");
|
||||
convertor.put(new Integer(235), "ë");
|
||||
convertor.put(new Integer(236), "ì");
|
||||
convertor.put(new Integer(237), "í");
|
||||
convertor.put(new Integer(238), "î");
|
||||
convertor.put(new Integer(239), "ï");
|
||||
convertor.put(new Integer(240), "ð");
|
||||
convertor.put(new Integer(241), "ñ");
|
||||
convertor.put(new Integer(242), "ò");
|
||||
convertor.put(new Integer(243), "ó");
|
||||
convertor.put(new Integer(244), "ô");
|
||||
convertor.put(new Integer(245), "õ");
|
||||
convertor.put(new Integer(246), "ö");
|
||||
convertor.put(new Integer(247), "÷");
|
||||
convertor.put(new Integer(248), "ø");
|
||||
convertor.put(new Integer(249), "ù");
|
||||
convertor.put(new Integer(250), "ú");
|
||||
convertor.put(new Integer(251), "û");
|
||||
convertor.put(new Integer(252), "ü");
|
||||
convertor.put(new Integer(253), "ý");
|
||||
convertor.put(new Integer(254), "þ");
|
||||
convertor.put(new Integer(255), "ÿ");
|
||||
} */
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -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
|
||||
|
|
|
@ -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 ();
|
||||
|
|
Loading…
Add table
Reference in a new issue