Actually update the atime of sessions.
[jsvc.git] / src / dolda / jsvc / util / Session.java
1 package dolda.jsvc.util;
2
3 import dolda.jsvc.*;
4 import java.util.*;
5 import java.security.SecureRandom;
6
7 public class Session implements java.io.Serializable {
8     private static final Map<String, Session> sessions = new HashMap<String, Session>();
9     private static final Map<Request, Session> cache = new WeakHashMap<Request, Session>();
10     private static final SecureRandom prng;
11     private static long lastclean = 0;
12     private final Map<Object, Object> props = new HashMap<Object, Object>();
13     private long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000;
14     private Collection<Listener> ll = new HashSet<Listener>();
15     
16     static {
17         try {
18             prng = SecureRandom.getInstance("SHA1PRNG");
19         } catch(java.security.NoSuchAlgorithmException e) {
20             throw(new Error(e));
21         }
22     }
23     
24     public static interface Listener {
25         public void expire(Session sess);
26     }
27     
28     public synchronized void listen(Listener l) {
29         ll.add(l);
30     }
31     
32     public synchronized Object get(Object key, Object def) {
33         if(props.containsKey(key))
34             return(props.get(key));
35         else
36             return(def);
37     }
38     
39     public synchronized Object put(Object key, Object val) {
40         return(props.put(key, val));
41     }
42     
43     private synchronized void expire() {
44         for(Listener l : ll)
45             l.expire(this);
46     }
47     
48     public synchronized static int num() {
49         return(sessions.size());
50     }
51
52     private static String newid() {
53         byte[] rawid = new byte[16];
54         prng.nextBytes(rawid);
55         StringBuilder buf = new StringBuilder();
56         for(byte b : rawid) {
57             buf.append(Misc.int2hex((b & 0xf0) >> 4, false));
58             buf.append(Misc.int2hex(b & 0x0f, false));
59         }
60         return(buf.toString());
61     }
62
63     private static Session create(Request req) {
64         Session sess = new Session();
65         long etime = 0;
66         int ct;
67         ct = Integer.parseInt(req.ctx().libconfig("jsvc.session.expire", "0"));
68         if(ct > 0)
69             sess.etime = ct;
70         ct = Integer.parseInt(req.ctx().sysconfig("jsvc.session.expire", "0"));
71         if(ct > 0)
72             sess.etime = ct;
73         return(sess);
74     }
75     
76     private synchronized static void clean() {
77         long now = System.currentTimeMillis();
78         for(Iterator<Session> i = sessions.values().iterator(); i.hasNext();) {
79             Session sess = i.next();
80             if(now > sess.atime + sess.etime) {
81                 i.remove();
82                 sess.expire();
83             }
84         }
85     }
86
87     public synchronized static Session get(Request req) {
88         long now = System.currentTimeMillis();
89         if(now - lastclean > 3600 * 1000) {
90             clean();
91             lastclean = now;
92         }
93         
94         Session sess = cache.get(req);
95         if(sess != null) {
96             sess.atime = System.currentTimeMillis();
97             return(sess);
98         }
99         
100         MultiMap<String, Cookie> cookies = Cookie.get(req);
101         Cookie sc = cookies.get("jsvc-session");
102
103         if(sc != null)
104             sess = sessions.get(sc.value);
105         if(sess == null) {
106             String id = newid();
107             sess = create(req);
108             sessions.put(id, sess);
109             sc = new Cookie("jsvc-session", id);
110             sc.expires = new Date(System.currentTimeMillis() + (86400L * 365L * 1000L));
111             sc.path = req.ctx().sysconfig("jsvc.session.path", req.rooturl().getPath());
112             String pd = req.ctx().sysconfig("jsvc.session.domain", null);
113             if(pd != null)
114                 sc.domain = pd;
115             sc.addto(req);
116         }
117         
118         cache.put(req, sess);
119         sess.atime = System.currentTimeMillis();
120         return(sess);
121     }
122     
123     public static Session get() {
124         return(get(RequestThread.request()));
125     }
126 }