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 | ||
55a299a0 FT |
7 | public abstract class Session implements java.io.Serializable { |
8 | public static final ContextParam<Storage> store = new ContextParam<Storage>(new MemoryStorage()); | |
48ea770f | 9 | private static final Map<Request, Session> cache = new WeakHashMap<Request, Session>(); |
3d90f73a | 10 | private final Map<Object, Object> props = new IdentityHashMap<Object, Object>(); |
55a299a0 | 11 | public long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000; |
d5dd6a2d FT |
12 | private Collection<Listener> ll = new HashSet<Listener>(); |
13 | ||
d5dd6a2d | 14 | public static interface Listener { |
f0f0cfd1 FT |
15 | public void destroy(Session sess); |
16 | } | |
17 | ||
55a299a0 FT |
18 | public static interface Storage { |
19 | public Session get(Request req); | |
d5dd6a2d FT |
20 | } |
21 | ||
55a299a0 FT |
22 | public static abstract class BaseStorage implements Storage { |
23 | private static final SecureRandom prng; | |
24 | ||
25 | static { | |
26 | try { | |
27 | prng = SecureRandom.getInstance("SHA1PRNG"); | |
28 | } catch(java.security.NoSuchAlgorithmException e) { | |
29 | throw(new Error(e)); | |
30 | } | |
31 | } | |
32 | ||
33 | public Session get(Request req) { | |
34 | MultiMap<String, Cookie> cookies = Cookie.get(req); | |
35 | Cookie sc = cookies.get("jsvc-session"); | |
36 | ||
37 | Session sess = null; | |
38 | if(sc != null) | |
39 | sess = get(sc.value); | |
40 | if(sess == null) { | |
41 | sess = create(req); | |
42 | sc = new Cookie("jsvc-session", sess.id()); | |
43 | sc.expires = new Date(System.currentTimeMillis() + (86400L * 365L * 1000L)); | |
44 | sc.path = req.ctx().sysconfig("jsvc.session.path", req.rooturl().getPath()); | |
45 | String pd = req.ctx().sysconfig("jsvc.session.domain", null); | |
46 | if(pd != null) | |
47 | sc.domain = pd; | |
48 | sc.addto(req); | |
49 | } | |
50 | return(sess); | |
51 | } | |
52 | ||
53 | protected abstract Session get(String id); | |
54 | protected abstract Session create(Request req); | |
55 | ||
56 | public static String newid() { | |
57 | byte[] rawid = new byte[16]; | |
58 | prng.nextBytes(rawid); | |
59 | StringBuilder buf = new StringBuilder(); | |
60 | for(byte b : rawid) { | |
61 | buf.append(Misc.int2hex((b & 0xf0) >> 4, false)); | |
62 | buf.append(Misc.int2hex(b & 0x0f, false)); | |
63 | } | |
64 | return(buf.toString()); | |
f0f0cfd1 | 65 | } |
d5dd6a2d FT |
66 | } |
67 | ||
55a299a0 FT |
68 | public static class MemoryStorage extends BaseStorage { |
69 | private final Map<Long, MemorySession> sessions = new HashMap<Long, MemorySession>(); | |
70 | private static long lastclean = 0; | |
71 | ||
72 | private class MemorySession extends Session { | |
73 | private final long id = BaseStorage.prng.nextLong(); | |
74 | ||
75 | private MemorySession(Request req) { | |
76 | super(req); | |
77 | } | |
78 | ||
79 | public void destroy() { | |
80 | synchronized(sessions) { | |
81 | sessions.remove(id); | |
82 | } | |
83 | super.destroy(); | |
84 | } | |
85 | ||
86 | private void expire() { | |
87 | super.destroy(); | |
88 | } | |
89 | ||
90 | public String id() { | |
91 | return(Long.toString(id)); | |
92 | } | |
93 | } | |
94 | ||
95 | public int num() { | |
96 | synchronized(sessions) { | |
97 | return(sessions.size()); | |
98 | } | |
99 | } | |
100 | ||
101 | public Session get(String id) { | |
102 | long idl; | |
103 | try { | |
104 | idl = Long.parseLong(id); | |
105 | } catch(NumberFormatException e) { | |
106 | return(null); | |
107 | } | |
108 | synchronized(sessions) { | |
109 | return(sessions.get(idl)); | |
110 | } | |
111 | } | |
112 | ||
113 | public synchronized Session create(Request req) { | |
114 | MemorySession sess = new MemorySession(req); | |
115 | synchronized(sessions) { | |
116 | sessions.put(sess.id, sess); | |
117 | } | |
118 | return(sess); | |
119 | } | |
d5dd6a2d | 120 | |
55a299a0 FT |
121 | private void clean() { |
122 | long now = System.currentTimeMillis(); | |
123 | synchronized(sessions) { | |
124 | for(Iterator<MemorySession> i = sessions.values().iterator(); i.hasNext();) { | |
125 | MemorySession sess = i.next(); | |
126 | if(now > sess.atime + sess.etime) { | |
127 | i.remove(); | |
128 | sess.expire(); | |
129 | } | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
134 | public Session get(Request req) { | |
135 | long now = System.currentTimeMillis(); | |
136 | if(now - lastclean > 3600 * 1000) { | |
137 | clean(); | |
138 | lastclean = now; | |
139 | } | |
140 | ||
141 | return(super.get(req)); | |
d5dd6a2d | 142 | } |
d5dd6a2d | 143 | } |
55a299a0 FT |
144 | |
145 | protected Session(Request req) { | |
d5dd6a2d FT |
146 | int ct; |
147 | ct = Integer.parseInt(req.ctx().libconfig("jsvc.session.expire", "0")); | |
148 | if(ct > 0) | |
55a299a0 | 149 | etime = ct; |
d5dd6a2d FT |
150 | ct = Integer.parseInt(req.ctx().sysconfig("jsvc.session.expire", "0")); |
151 | if(ct > 0) | |
55a299a0 | 152 | etime = ct; |
d5dd6a2d FT |
153 | } |
154 | ||
55a299a0 FT |
155 | public abstract String id(); |
156 | ||
157 | public void listen(Listener l) { | |
158 | synchronized(ll) { | |
159 | ll.add(l); | |
d5dd6a2d FT |
160 | } |
161 | } | |
55a299a0 FT |
162 | |
163 | public Object get(Object key, Object def) { | |
164 | synchronized(props) { | |
165 | if(props.containsKey(key)) | |
166 | return(props.get(key)); | |
167 | else | |
168 | return(def); | |
d5dd6a2d | 169 | } |
55a299a0 FT |
170 | } |
171 | ||
172 | public synchronized Object put(Object key, Object val) { | |
173 | return(props.put(key, val)); | |
174 | } | |
175 | ||
176 | public void destroy() { | |
177 | synchronized(ll) { | |
178 | for(Listener l : ll) | |
179 | l.destroy(this); | |
70c1fc5a | 180 | } |
55a299a0 FT |
181 | } |
182 | ||
183 | public static Session get(Request req) { | |
184 | Session sess; | |
185 | synchronized(req) { | |
186 | synchronized(cache) { | |
187 | sess = cache.get(req); | |
188 | } | |
189 | if(sess == null) { | |
190 | sess = store.get().get(req); | |
191 | synchronized(cache) { | |
192 | cache.put(req, sess); | |
193 | } | |
194 | } | |
195 | sess.atime = System.currentTimeMillis(); | |
d5dd6a2d FT |
196 | } |
197 | return(sess); | |
198 | } | |
199 | ||
200 | public static Session get() { | |
201 | return(get(RequestThread.request())); | |
202 | } | |
203 | } |