Merge branch 'master' of git.dolda2000.com:/srv/git/r/jsvc master
authorFredrik Tolf <fredrik@dolda2000.com>
Tue, 29 Mar 2011 06:21:30 +0000 (08:21 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Tue, 29 Mar 2011 06:21:30 +0000 (08:21 +0200)
src/dolda/jsvc/ContextResponder.java [deleted file]
src/dolda/jsvc/Destroyable.java [new file with mode: 0644]
src/dolda/jsvc/ThreadContext.java
src/dolda/jsvc/util/PerSession.java
src/dolda/jsvc/util/Session.java

diff --git a/src/dolda/jsvc/ContextResponder.java b/src/dolda/jsvc/ContextResponder.java
deleted file mode 100644 (file)
index 1ff7b44..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package dolda.jsvc;
-
-public interface ContextResponder extends Responder {
-    public void destroy();
-}
diff --git a/src/dolda/jsvc/Destroyable.java b/src/dolda/jsvc/Destroyable.java
new file mode 100644 (file)
index 0000000..deb90eb
--- /dev/null
@@ -0,0 +1,5 @@
+package dolda.jsvc;
+
+public interface Destroyable {
+    public void destroy();
+}
index 8e544d3..6eb7d87 100644 (file)
@@ -118,8 +118,8 @@ public class ThreadContext extends ThreadGroup {
     }
     
     public void shutdown() {
-       if(root instanceof ContextResponder)
-           ((ContextResponder)root).destroy();
+       if(root instanceof Destroyable)
+           ((Destroyable)root).destroy();
        try {
            long last = 0;
            while(true) {
index 562e2c9..d4e4d73 100644 (file)
@@ -93,14 +93,6 @@ public class PerSession implements Responder {
            if(resp == null) {
                resp = create(sess);
                sess.put(rcl, resp);
-               if(resp instanceof ContextResponder) {
-                   final ContextResponder cr = (ContextResponder)resp;
-                   sess.listen(new Session.Listener() {
-                           public void destroy(Session sess) {
-                               cr.destroy();
-                           }
-                       });
-               }
            }
        }
        resp.respond(req);
index eb17fcf..7b87d12 100644 (file)
@@ -4,41 +4,158 @@ import dolda.jsvc.*;
 import java.util.*;
 import java.security.SecureRandom;
 
-public class Session implements java.io.Serializable {
-    private static final Map<String, Session> sessions = new HashMap<String, Session>();
+public abstract class Session implements java.io.Serializable {
+    public static final ContextParam<Storage> store = new ContextParam<Storage>(new MemoryStorage());
     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 IdentityHashMap<Object, Object>();
-    private long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000;
-    private Collection<Listener> ll = new HashSet<Listener>();
+    public long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000;
     
-    static {
-       try {
-           prng = SecureRandom.getInstance("SHA1PRNG");
-       } catch(java.security.NoSuchAlgorithmException e) {
-           throw(new Error(e));
-       }
+    public static interface Storage {
+       public Session get(Request req);
     }
     
-    public static interface Listener {
-       public void destroy(Session sess);
+    public static abstract class BaseStorage implements Storage {
+       private static final SecureRandom prng;
+       
+       static {
+           try {
+               prng = SecureRandom.getInstance("SHA1PRNG");
+           } catch(java.security.NoSuchAlgorithmException e) {
+               throw(new Error(e));
+           }
+       }
+       
+       public Session get(Request req) {
+           MultiMap<String, Cookie> cookies = Cookie.get(req);
+           Cookie sc = cookies.get("jsvc-session");
+
+           Session sess = null;
+           if(sc != null)
+               sess = get(sc.value);
+           if(sess == null) {
+               sess = create(req);
+               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);
+           }
+           return(sess);
+       }
+       
+       protected abstract Session get(String id);
+       protected abstract Session create(Request req);
+
+       public static String newid() {
+           byte[] rawid = new byte[16];
+           prng.nextBytes(rawid);
+           StringBuilder buf = new StringBuilder();
+           for(byte b : rawid) {
+               buf.append(Misc.int2hex((b & 0xf0) >> 4, false));
+               buf.append(Misc.int2hex(b & 0x0f, false));
+           }
+           return(buf.toString());
+       }
     }
     
-    private Session(String id) {
-       this.id = id;
+    public static class MemoryStorage extends BaseStorage {
+       private final Map<Long, MemorySession> sessions = new HashMap<Long, MemorySession>();
+       private static long lastclean = 0;
+       
+       private class MemorySession extends Session {
+           private final long id = BaseStorage.prng.nextLong();
+           
+           private MemorySession(Request req) {
+               super(req);
+           }
+           
+           public void destroy() {
+               synchronized(sessions) {
+                   sessions.remove(id);
+               }
+               super.destroy();
+           }
+           
+           private void expire() {
+               super.destroy();
+           }
+           
+           public String id() {
+               return(Long.toString(id));
+           }
+       }
+       
+       public int num() {
+           synchronized(sessions) {
+               return(sessions.size());
+           }
+       }
+       
+       public Session get(String id) {
+           long idl;
+           try {
+               idl = Long.parseLong(id);
+           } catch(NumberFormatException e) {
+               return(null);
+           }
+           synchronized(sessions) {
+               return(sessions.get(idl));
+           }
+       }
+       
+       public synchronized Session create(Request req) {
+           MemorySession sess = new MemorySession(req);
+           synchronized(sessions) {
+               sessions.put(sess.id, sess);
+           }
+           return(sess);
+       }
+
+       private void clean() {
+           long now = System.currentTimeMillis();
+           synchronized(sessions) {
+               for(Iterator<MemorySession> i = sessions.values().iterator(); i.hasNext();) {
+                   MemorySession sess = i.next();
+                   if(now > sess.atime + sess.etime) {
+                       i.remove();
+                       sess.expire();
+                   }
+               }
+           }
+       }
+       
+       public Session get(Request req) {
+           long now = System.currentTimeMillis();
+           if(now - lastclean > 3600 * 1000) {
+               clean();
+               lastclean = now;
+           }
+       
+           return(super.get(req));
+       }
     }
     
-    public synchronized void listen(Listener l) {
-       ll.add(l);
+    protected Session(Request req) {
+       int ct;
+       ct = Integer.parseInt(req.ctx().libconfig("jsvc.session.expire", "0"));
+       if(ct > 0)
+           etime = ct;
+       ct = Integer.parseInt(req.ctx().sysconfig("jsvc.session.expire", "0"));
+       if(ct > 0)
+           etime = ct;
     }
     
-    public synchronized Object get(Object key, Object def) {
-       if(props.containsKey(key))
-           return(props.get(key));
-       else
-           return(def);
+    public abstract String id();
+    
+    public Object get(Object key, Object def) {
+       synchronized(props) {
+           if(props.containsKey(key))
+               return(props.get(key));
+           else
+               return(def);
+       }
     }
     
     public synchronized Object put(Object key, Object val) {
@@ -46,88 +163,28 @@ public class Session implements java.io.Serializable {
     }
     
     public void destroy() {
-       synchronized(Session.class) {
-           sessions.remove(id);
+       synchronized(props) {
+           for(Object val : props.values()) {
+               if(val instanceof Destroyable)
+                   ((Destroyable)val).destroy();
+           }
        }
-       expire();
     }
     
-    private synchronized void expire() {
-       for(Listener l : ll)
-           l.destroy(this);
-    }
-    
-    public synchronized static int num() {
-       return(sessions.size());
-    }
-
-    private static String newid() {
-       byte[] rawid = new byte[16];
-       prng.nextBytes(rawid);
-       StringBuilder buf = new StringBuilder();
-       for(byte b : rawid) {
-           buf.append(Misc.int2hex((b & 0xf0) >> 4, false));
-           buf.append(Misc.int2hex(b & 0x0f, false));
-       }
-       return(buf.toString());
-    }
-
-    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"));
-       if(ct > 0)
-           sess.etime = ct;
-       ct = Integer.parseInt(req.ctx().sysconfig("jsvc.session.expire", "0"));
-       if(ct > 0)
-           sess.etime = ct;
-       return(sess);
-    }
-    
-    private synchronized static void clean() {
-       long now = System.currentTimeMillis();
-       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) {
+       Session sess;
+       synchronized(req) {
+           synchronized(cache) {
+               sess = cache.get(req);
+           }
+           if(sess == null) {
+               sess = store.get().get(req);
+               synchronized(cache) {
+                   cache.put(req, sess);
+               }
            }
-       }
-    }
-
-    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");
-
-       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);
     }