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