From aaeba92e4b0c8bc2783f6438249303785ef6cea7 Mon Sep 17 00:00:00 2001 From: hns Date: Wed, 16 Mar 2005 17:32:32 +0000 Subject: [PATCH] Swap out session handling into separate SessionManager class. --- src/helma/framework/core/Application.java | 230 ++++------------ src/helma/framework/core/ApplicationBean.java | 17 +- src/helma/framework/core/SessionManager.java | 255 ++++++++++++++++++ 3 files changed, 307 insertions(+), 195 deletions(-) create mode 100644 src/helma/framework/core/SessionManager.java diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java index 57352ae7..6852f728 100644 --- a/src/helma/framework/core/Application.java +++ b/src/helma/framework/core/Application.java @@ -69,7 +69,10 @@ public final class Application implements IPathElement, Runnable { Object rootObject = null; String rootObjectClass; - /** + // The session manager + SessionManager sessionMgr; + + /** * The type manager checks if anything in the application's prototype definitions * has been updated prior to each evaluation. */ @@ -88,7 +91,6 @@ public final class Application implements IPathElement, Runnable { boolean running = false; boolean debug; long starttime; - Hashtable sessions; Hashtable dbSources; // map of app modules reflected at app.modules @@ -257,7 +259,6 @@ public final class Application implements IPathElement, Runnable { updateProperties(); - sessions = new Hashtable(); dbSources = new Hashtable(); modules = new SystemMap(); @@ -271,6 +272,17 @@ public final class Application implements IPathElement, Runnable { throws DatabaseException, IllegalAccessException, InstantiationException, ClassNotFoundException { + // create and init session manager + String sessionMgrImpl = props.getProperty("sessionManagerImpl", + "helma.framework.core.SessionManager"); + sessionMgr = (SessionManager) Class.forName(sessionMgrImpl).newInstance(); + sessionMgr.setApplication(this); + + // read the sessions if wanted + if ("true".equalsIgnoreCase(getProperty("persistentSessions"))) { + sessionMgr.loadSessionData(null); + } + // create and init type mananger typemgr = new TypeManager(this); typemgr.createPrototypes(); @@ -295,11 +307,6 @@ public final class Application implements IPathElement, Runnable { } } - // read the sessions if wanted - if ("true".equalsIgnoreCase(getProperty("persistentSessions"))) { - loadSessionData(null); - } - // create and init evaluator/thread lists freeThreads = new Stack(); allThreads = new Vector(); @@ -406,9 +413,6 @@ public final class Application implements IPathElement, Runnable { System.err.println("Error shutting down embedded db: " + dbx); } - // null out type manager - typemgr = null; - // tell the extensions that we're stopped. if (Server.getServer() != null) { Vector extensions = Server.getServer().getExtensions(); @@ -422,7 +426,7 @@ public final class Application implements IPathElement, Runnable { // store the sessions if wanted if ("true".equalsIgnoreCase(getProperty("persistentSessions"))) { - storeSessionData(null); + sessionMgr.storeSessionData(null); } } @@ -564,7 +568,7 @@ public final class Application implements IPathElement, Runnable { requestCount += 1; // get user for this request's session - Session session = checkSession(req.session); + Session session = createSession(req.session); session.touch(); @@ -829,40 +833,8 @@ public final class Application implements IPathElement, Runnable { * Return the session currently associated with a given Hop session ID. * Create a new session if necessary. */ - public Session checkSession(String sessionID) { - Session session = getSession(sessionID); - - if (session == null) { - session = new Session(sessionID, this); - sessions.put(sessionID, session); - } - - 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(); + public Session createSession(String sessionId) { + return sessionMgr.createSession(sessionId); } /** @@ -870,33 +842,7 @@ public final class Application implements IPathElement, Runnable { * 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 sessionMgr.getActiveUsers(); } /** @@ -923,39 +869,32 @@ public final class Application implements IPathElement, Runnable { } /** - * Return an array of SessionBean objects currently associated with a given - * Helma user. + * Return an array of SessionBean 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 sessionMgr.getSessionsForUsername(username); } /** * Return the session currently associated with a given Hop session ID. */ - public Session getSession(String sessionID) { - if (sessionID == null) { - return null; - } + public Session getSession(String sessionId) { + return sessionMgr.getSession(sessionId); + } - return (Session) sessions.get(sessionID); + /** + * Return the whole session map. + */ + public Map getSessions() { + return sessionMgr.getSessions(); + } + + /** + * Returns the number of currenty active sessions. + */ + public int countSessions() { + return sessionMgr.countSessions(); } /** @@ -1027,11 +966,14 @@ public final class Application implements IPathElement, Runnable { try { INode users = getUserRoot(); Node unode = (Node) users.getChildElement(uname); + if (unode == null) + return false; + String pw = unode.getString("password"); if ((pw != null) && pw.equals(password)) { // let the old user-object forget about this session - logoutSession(session); + session.logout(); session.login(unode); return true; @@ -1439,10 +1381,11 @@ public final class Application implements IPathElement, Runnable { thisEvaluator = getEvaluator(); - Hashtable cloned = (Hashtable) sessions.clone(); + Map sessions = sessionMgr.getSessions(); - for (Enumeration e = cloned.elements(); e.hasMoreElements();) { - Session session = (Session) e.nextElement(); + Iterator it = sessions.values().iterator(); + while (it.hasNext()) { + Session session = (Session) it.next(); if ((now - session.lastTouched()) > (sessionTimeout * 60000)) { NodeHandle userhandle = session.userHandle; @@ -1456,7 +1399,7 @@ public final class Application implements IPathElement, Runnable { } } - destroySession(session); + sessionMgr.discardSession(session); } } } catch (Exception cx) { @@ -1891,85 +1834,6 @@ public final class Application implements IPathElement, Runnable { } } - /** - * - * - * @param f ... - */ - public void storeSessionData(File f) { - if (f == null) { - f = new File(dbDir, "sessions"); - } - - try { - OutputStream ostream = new BufferedOutputStream(new FileOutputStream(f)); - ObjectOutputStream p = new ObjectOutputStream(ostream); - - synchronized (sessions) { - p.writeInt(sessions.size()); - - for (Enumeration e = sessions.elements(); e.hasMoreElements();) { - p.writeObject(e.nextElement()); - } - } - - p.flush(); - ostream.close(); - logEvent("stored " + sessions.size() + " sessions in file"); - } catch (Exception e) { - logEvent("error storing session data: " + e.toString()); - } - } - - /** - * loads the serialized session table from a given file or from dbdir/sessions - */ - public void loadSessionData(File f) { - if (f == null) { - f = new File(dbDir, "sessions"); - } - - // compute session timeout value - int sessionTimeout = 30; - - try { - sessionTimeout = Math.max(0, - Integer.parseInt(props.getProperty("sessionTimeout", - "30"))); - } catch (Exception ignore) { - System.out.println(ignore.toString()); - } - - long now = System.currentTimeMillis(); - - try { - // load the stored data: - InputStream istream = new BufferedInputStream(new FileInputStream(f)); - ObjectInputStream p = new ObjectInputStream(istream); - int size = p.readInt(); - int ct = 0; - Hashtable newSessions = new Hashtable(); - - while (ct < size) { - Session session = (Session) p.readObject(); - - if ((now - session.lastTouched()) < (sessionTimeout * 60000)) { - session.setApp(this); - newSessions.put(session.getSessionID(), session); - } - - ct++; - } - - p.close(); - istream.close(); - sessions = newSessions; - logEvent("loaded " + newSessions.size() + " sessions from file"); - } catch (Exception e) { - logEvent("error loading session data: " + e.toString()); - } - } - class CronRunner extends Thread { RequestEvaluator thisEvaluator; CronJob job; diff --git a/src/helma/framework/core/ApplicationBean.java b/src/helma/framework/core/ApplicationBean.java index 615247ca..aeb87e61 100644 --- a/src/helma/framework/core/ApplicationBean.java +++ b/src/helma/framework/core/ApplicationBean.java @@ -108,7 +108,7 @@ public class ApplicationBean implements Serializable { * @return ... */ public int countSessions() { - return app.sessions.size(); + return app.countSessions(); } /** @@ -144,7 +144,7 @@ public class ApplicationBean implements Serializable { return null; } - Session session = app.checkSession(sessionID.trim()); + Session session = app.createSession(sessionID.trim()); if (session == null) { return null; @@ -159,16 +159,9 @@ public class ApplicationBean implements Serializable { * @return ... */ 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; + Map sessions = app.getSessions(); + Object[] array = new SessionBean[sessions.size()]; + return (SessionBean[]) sessions.values().toArray(array); } /** diff --git a/src/helma/framework/core/SessionManager.java b/src/helma/framework/core/SessionManager.java new file mode 100644 index 00000000..a9af1a23 --- /dev/null +++ b/src/helma/framework/core/SessionManager.java @@ -0,0 +1,255 @@ +/* + * Helma License Notice + * + * The contents of this file are subject to the Helma License + * Version 2.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://adele.helma.org/download/helma/license.txt + * + * Copyright 1998-2003 Helma Software. All Rights Reserved. + * + * $RCSfile$ + * $Author$ + * $Revision$ + * $Date$ + */ +package helma.framework.core; + +import helma.objectmodel.INode; +import helma.objectmodel.db.Node; + +import java.util.*; +import java.io.*; + +public class SessionManager { + + protected Hashtable sessions; + + protected Application app; + + public SessionManager() { + sessions = new Hashtable(); + } + + public void setApplication(Application app) { + this.app = app; + } + + public Session createSession(String sessionId) { + Session session = getSession(sessionId); + + if (session == null) { + session = new Session(sessionId, app); + sessions.put(sessionId, session); + } + + return session; + } + + public Session getSession(String sessionId) { + if (sessionId == null) + return null; + + return (Session) sessions.get(sessionId); + } + + /** + * 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(); + } + + /** + * Returns the number of currenty active sessions. + */ + public int countSessions() { + return sessions.size(); + } + + /** + * Remove the session from the sessions-table and logout the user. + */ + public void discardSession(Session session) { + logoutSession(session); + sessions.remove(session.getSessionID()); + } + + /** + * Log in a user given his or her user name and password. + */ + 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 = app.getUserRoot(); + Node unode = (Node) users.getChildElement(uname); + String pw = unode.getString("password"); + + if ((pw != null) && pw.equals(password)) { + // let the old user-object forget about this session + logoutSession(session); + session.login(unode); + + return true; + } + } catch (Exception x) { + return false; + } + + return false; + } + + /** + * Log out a session from this application. + */ + public void logoutSession(Session session) { + session.logout(); + } + + + /** + * Return an array of SessionBean objects currently associated with a given + * Helma user. + */ + public List getSessionsForUsername(String username) { + ArrayList list = new ArrayList(); + + if (username == null) { + return list; + } + + Enumeration e = sessions.elements(); + while (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 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(); + + for (Enumeration e = sessions.elements(); e.hasMoreElements();) { + Session s = (Session) e.nextElement(); + + if (s == null) { + continue; + } else if (s.isLoggedIn()) { + // returns a session if it is logged in and has not been + // returned before (so for each logged-in user is only added once) + INode node = s.getUserNode(); + + // we check again because user may have been logged out between the first check + if (node != null && !list.contains(node)) { + list.add(node); + } + } + } + + return list; + } + + + /** + * + * + * @param f ... + */ + public void storeSessionData(File f) { + if (f == null) { + f = new File(app.dbDir, "sessions"); + } + + try { + OutputStream ostream = new BufferedOutputStream(new FileOutputStream(f)); + ObjectOutputStream p = new ObjectOutputStream(ostream); + + synchronized (sessions) { + p.writeInt(sessions.size()); + + for (Enumeration e = sessions.elements(); e.hasMoreElements();) { + p.writeObject(e.nextElement()); + } + } + + p.flush(); + ostream.close(); + app.logEvent("stored " + sessions.size() + " sessions in file"); + } catch (Exception e) { + app.logEvent("error storing session data: " + e.toString()); + } + } + + /** + * loads the serialized session table from a given file or from dbdir/sessions + */ + public void loadSessionData(File f) { + if (f == null) { + f = new File(app.dbDir, "sessions"); + } + + // compute session timeout value + int sessionTimeout = 30; + + try { + sessionTimeout = Math.max(0, + Integer.parseInt(app.getProperty("sessionTimeout", + "30"))); + } catch (Exception ignore) { + System.out.println(ignore.toString()); + } + + long now = System.currentTimeMillis(); + + try { + // load the stored data: + InputStream istream = new BufferedInputStream(new FileInputStream(f)); + ObjectInputStream p = new ObjectInputStream(istream); + int size = p.readInt(); + int ct = 0; + Hashtable newSessions = new Hashtable(); + + while (ct < size) { + Session session = (Session) p.readObject(); + + if ((now - session.lastTouched()) < (sessionTimeout * 60000)) { + session.setApp(app); + newSessions.put(session.getSessionID(), session); + } + + ct++; + } + + p.close(); + istream.close(); + sessions = newSessions; + app.logEvent("loaded " + newSessions.size() + " sessions from file"); + } catch (Exception e) { + app.logEvent("error loading session data: " + e.toString()); + } + } + +}