Actually update the atime of sessions.
[jsvc.git] / src / dolda / jsvc / util / Session.java
CommitLineData
d5dd6a2d
FT
1package dolda.jsvc.util;
2
3import dolda.jsvc.*;
4import java.util.*;
5import java.security.SecureRandom;
6
7public class Session implements java.io.Serializable {
8 private static final Map<String, Session> sessions = new HashMap<String, Session>();
48ea770f 9 private static final Map<Request, Session> cache = new WeakHashMap<Request, Session>();
d5dd6a2d
FT
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
48ea770f
FT
28 public synchronized void listen(Listener l) {
29 ll.add(l);
d5dd6a2d
FT
30 }
31
48ea770f
FT
32 public synchronized Object get(Object key, Object def) {
33 if(props.containsKey(key))
34 return(props.get(key));
35 else
36 return(def);
d5dd6a2d
FT
37 }
38
48ea770f
FT
39 public synchronized Object put(Object key, Object val) {
40 return(props.put(key, val));
d5dd6a2d
FT
41 }
42
48ea770f
FT
43 private synchronized void expire() {
44 for(Listener l : ll)
45 l.expire(this);
d5dd6a2d
FT
46 }
47
48ea770f
FT
48 public synchronized static int num() {
49 return(sessions.size());
d5dd6a2d
FT
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
48ea770f 76 private synchronized static void clean() {
d5dd6a2d 77 long now = System.currentTimeMillis();
48ea770f
FT
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();
d5dd6a2d
FT
83 }
84 }
85 }
86
48ea770f 87 public synchronized static Session get(Request req) {
d5dd6a2d
FT
88 long now = System.currentTimeMillis();
89 if(now - lastclean > 3600 * 1000) {
90 clean();
91 lastclean = now;
92 }
93
48ea770f 94 Session sess = cache.get(req);
70c1fc5a
FT
95 if(sess != null) {
96 sess.atime = System.currentTimeMillis();
48ea770f 97 return(sess);
70c1fc5a 98 }
48ea770f 99
d5dd6a2d
FT
100 MultiMap<String, Cookie> cookies = Cookie.get(req);
101 Cookie sc = cookies.get("jsvc-session");
48ea770f
FT
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);
d5dd6a2d 116 }
48ea770f
FT
117
118 cache.put(req, sess);
70c1fc5a 119 sess.atime = System.currentTimeMillis();
d5dd6a2d
FT
120 return(sess);
121 }
122
123 public static Session get() {
124 return(get(RequestThread.request()));
125 }
126}