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>(); | |
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 | } |