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