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