Added an explicit destruction action for sessions.
[jsvc.git] / src / dolda / jsvc / util / Session.java
index 31b65a7..679a72f 100644 (file)
@@ -6,8 +6,10 @@ import java.security.SecureRandom;
 
 public class Session implements java.io.Serializable {
     private static final Map<String, Session> sessions = new HashMap<String, Session>();
+    private static final Map<Request, Session> cache = new WeakHashMap<Request, Session>();
     private static final SecureRandom prng;
     private static long lastclean = 0;
+    private final String id;
     private final Map<Object, Object> props = new HashMap<Object, Object>();
     private long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000;
     private Collection<Listener> ll = new HashSet<Listener>();
@@ -21,41 +23,42 @@ public class Session implements java.io.Serializable {
     }
     
     public static interface Listener {
-       public void expire(Session sess);
+       public void destroy(Session sess);
     }
     
-    public void listen(Listener l) {
-       synchronized(ll) {
-           ll.add(l);
-       }
+    private Session(String id) {
+       this.id = id;
     }
     
-    public Object get(Object key, Object def) {
-       synchronized(props) {
-           if(props.containsKey(key))
-               return(props.get(key));
-           else
-               return(def);
-       }
+    public synchronized void listen(Listener l) {
+       ll.add(l);
     }
     
-    public Object put(Object key, Object val) {
-       synchronized(props) {
-           return(props.put(key, val));
-       }
+    public synchronized Object get(Object key, Object def) {
+       if(props.containsKey(key))
+           return(props.get(key));
+       else
+           return(def);
     }
     
-    private void expire() {
-       synchronized(ll) {
-           for(Listener l : ll)
-               l.expire(this);
-       }
+    public synchronized Object put(Object key, Object val) {
+       return(props.put(key, val));
     }
     
-    public static int num() {
-       synchronized(sessions) {
-           return(sessions.size());
+    public void destroy() {
+       synchronized(Session.class) {
+           sessions.remove(id);
        }
+       expire();
+    }
+    
+    private synchronized void expire() {
+       for(Listener l : ll)
+           l.destroy(this);
+    }
+    
+    public synchronized static int num() {
+       return(sessions.size());
     }
 
     private static String newid() {
@@ -69,8 +72,8 @@ public class Session implements java.io.Serializable {
        return(buf.toString());
     }
 
-    private static Session create(Request req) {
-       Session sess = new Session();
+    private static Session create(Request req, String id) {
+       Session sess = new Session(id);
        long etime = 0;
        int ct;
        ct = Integer.parseInt(req.ctx().libconfig("jsvc.session.expire", "0"));
@@ -82,45 +85,49 @@ public class Session implements java.io.Serializable {
        return(sess);
     }
     
-    private static void clean() {
+    private synchronized static void clean() {
        long now = System.currentTimeMillis();
-       synchronized(sessions) {
-           for(Iterator<Session> i = sessions.values().iterator(); i.hasNext();) {
-               Session sess = i.next();
-               if(now > sess.atime + sess.etime) {
-                   i.remove();
-                   sess.expire();
-               }
+       for(Iterator<Session> i = sessions.values().iterator(); i.hasNext();) {
+           Session sess = i.next();
+           if(now > sess.atime + sess.etime) {
+               i.remove();
+               sess.expire();
            }
        }
     }
 
-    public static Session get(Request req) {
+    public synchronized static Session get(Request req) {
        long now = System.currentTimeMillis();
        if(now - lastclean > 3600 * 1000) {
            clean();
            lastclean = now;
        }
        
+       Session sess = cache.get(req);
+       if(sess != null) {
+           sess.atime = System.currentTimeMillis();
+           return(sess);
+       }
+       
        MultiMap<String, Cookie> cookies = Cookie.get(req);
        Cookie sc = cookies.get("jsvc-session");
-       Session sess = null;
-       synchronized(sessions) {
-           if(sc != null)
-               sess = sessions.get(sc.value);
-           if(sess == null) {
-               String id = newid();
-               sess = create(req);
-               sessions.put(id, sess);
-               sc = new Cookie("jsvc-session", id);
-               sc.expires = new Date(System.currentTimeMillis() + (86400L * 365L * 1000L));
-               sc.path = req.ctx().sysconfig("jsvc.session.path", req.rooturl().getPath());
-               String pd = req.ctx().sysconfig("jsvc.session.domain", null);
-               if(pd != null)
-                   sc.domain = pd;
-               sc.addto(req);
-           }
+
+       if(sc != null)
+           sess = sessions.get(sc.value);
+       if(sess == null) {
+           sess = create(req, newid());
+           sessions.put(sess.id, sess);
+           sc = new Cookie("jsvc-session", sess.id);
+           sc.expires = new Date(System.currentTimeMillis() + (86400L * 365L * 1000L));
+           sc.path = req.ctx().sysconfig("jsvc.session.path", req.rooturl().getPath());
+           String pd = req.ctx().sysconfig("jsvc.session.domain", null);
+           if(pd != null)
+               sc.domain = pd;
+           sc.addto(req);
        }
+       
+       cache.put(req, sess);
+       sess.atime = System.currentTimeMillis();
        return(sess);
     }