Commit | Line | Data |
---|---|---|
d5dd6a2d FT |
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>(); | |
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 | } |